pax_global_header 0000666 0000000 0000000 00000000064 12716155161 0014517 g ustar 00root root 0000000 0000000 52 comment=a3a0acb982a87c5e8d92d5206dd75f4939bf2761
edge-addition-planarity-suite-Version_3.0.0.5/ 0000775 0000000 0000000 00000000000 12716155161 0021214 5 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/.gitignore 0000664 0000000 0000000 00000000426 12716155161 0023206 0 ustar 00root root 0000000 0000000 # Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
edge-addition-planarity-suite-Version_3.0.0.5/.project 0000664 0000000 0000000 00000000333 12716155161 0022662 0 ustar 00root root 0000000 0000000
Planarity-trunk
edge-addition-planarity-suite-Version_3.0.0.5/LICENSE.TXT 0000664 0000000 0000000 00000004760 12716155161 0022706 0 ustar 00root root 0000000 0000000 The Edge Addition Planarity Suite
Copyright (c) 1997-2015, John M. Boyer
All rights reserved. Includes a reference implementation of the following:
* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm".
Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012.
http://www.jgaa.info/16/268.html
* John M. Boyer. "A New Method for Efficiently Generating Planar Graph
Visibility Representations". In P. Eades and P. Healy, editors,
Proceedings of the 13th International Conference on Graph Drawing 2005,
Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.
* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)
Planarity by Edge Addition". Journal of Graph Algorithms and Applications,
Vol. 8, No. 3, pp. 241-273, 2004.
http://www.jgaa.info/08/91.html
* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,
Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,
University of Victoria, 2001.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of The Edge Addition Planarity Suite nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
edge-addition-planarity-suite-Version_3.0.0.5/Makefile.am 0000664 0000000 0000000 00000002446 12716155161 0023256 0 ustar 00root root 0000000 0000000 lib_LTLIBRARIES = libplanarity.la
libplanarity_la_SOURCES = \
c/graphDFSUtils.c \
c/graphDrawPlanar.c \
c/graphDrawPlanar_Extensions.c \
c/graphEmbed.c \
c/graphExtensions.c \
c/graphIO.c \
c/graphIsolator.c \
c/graphK23Search.c \
c/graphK23Search_Extensions.c \
c/graphK33Search.c \
c/graphK33Search_Extensions.c \
c/graphK4Search.c \
c/graphK4Search_Extensions.c \
c/graphNonplanar.c \
c/graphOuterplanarObstruction.c \
c/graphTests.c \
c/graphUtils.c \
c/listcoll.c \
c/planarityRandomGraphs.c \
c/planaritySpecificGraph.c \
c/planarityUtils.c \
c/stack.c
libplanarity_la_LDFLAGS = -no-undefined $(AM_LDFLAGS) -version-info @LT_CURRENT@:@LT_REVISION@:@LT_AGE@
headersdir = $(includedir)/planarity/
headers_HEADERS = \
c/appconst.h \
c/graph.h \
c/graphDrawPlanar.h \
c/graphDrawPlanar.private.h \
c/graphExtensions.h \
c/graphExtensions.private.h \
c/graphFunctionTable.h \
c/graphK23Search.h \
c/graphK23Search.private.h \
c/graphK33Search.h \
c/graphK33Search.private.h \
c/graphK4Search.h \
c/graphK4Search.private.h \
c/graphStructures.h \
c/listcoll.h \
c/planarity.h \
c/platformTime.h \
c/stack.h
bin_PROGRAMS = planarity
planarity_LDADD = libplanarity.la
planarity_SOURCES = c/planarity.c c/planarityCommandLine.c
man1_MANS = planarity.1
EXTRA_DIST=$(man1_MANS)
edge-addition-planarity-suite-Version_3.0.0.5/autogen.sh 0000775 0000000 0000000 00000000156 12716155161 0023217 0 ustar 00root root 0000000 0000000 #!/bin/sh
aclocal &&
autoconf &&
libtoolize --copy &&
automake --add-missing --copy &&
rm -rf autom4te.cache
edge-addition-planarity-suite-Version_3.0.0.5/c/ 0000775 0000000 0000000 00000000000 12716155161 0021436 5 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/.cproject 0000664 0000000 0000000 00000055342 12716155161 0023261 0 ustar 00root root 0000000 0000000
edge-addition-planarity-suite-Version_3.0.0.5/c/.project 0000664 0000000 0000000 00000004075 12716155161 0023113 0 ustar 00root root 0000000 0000000
Planarity-Corg.eclipse.cdt.managedbuilder.core.genmakebuilderclean,full,incremental,?name?org.eclipse.cdt.make.core.append_environmenttrueorg.eclipse.cdt.make.core.buildArgumentsorg.eclipse.cdt.make.core.buildCommandmakeorg.eclipse.cdt.make.core.buildLocation${workspace_loc:/Planarity-C/Debug}org.eclipse.cdt.make.core.contentsorg.eclipse.cdt.make.core.activeConfigSettingsorg.eclipse.cdt.make.core.enableAutoBuildfalseorg.eclipse.cdt.make.core.enableCleanBuildtrueorg.eclipse.cdt.make.core.enableFullBuildtrueorg.eclipse.cdt.make.core.stopOnErrortrueorg.eclipse.cdt.make.core.useDefaultBuildCmdtrueorg.eclipse.cdt.managedbuilder.core.ScannerConfigBuilderorg.eclipse.cdt.managedbuilder.core.ScannerConfigNatureorg.eclipse.cdt.managedbuilder.core.managedBuildNatureorg.eclipse.cdt.core.cnature
edge-addition-planarity-suite-Version_3.0.0.5/c/.settings/ 0000775 0000000 0000000 00000000000 12716155161 0023354 5 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/.settings/org.eclipse.cdt.managedbuilder.core.prefs 0000664 0000000 0000000 00000004460 12716155161 0033275 0 ustar 00root root 0000000 0000000 #Sat Sep 25 07:24:51 PDT 2010
eclipse.preferences.version=1
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/CPATH/delimiter=;
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/CPATH/operation=remove
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/C_INCLUDE_PATH/delimiter=;
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/C_INCLUDE_PATH/operation=remove
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/append=true
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/appendContributed=true
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/CPATH/delimiter=;
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/CPATH/operation=remove
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/C_INCLUDE_PATH/delimiter=;
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/C_INCLUDE_PATH/operation=remove
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/append=true
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/appendContributed=true
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/LIBRARY_PATH/delimiter=;
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/LIBRARY_PATH/operation=remove
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/append=true
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.44566100/appendContributed=true
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/LIBRARY_PATH/delimiter=;
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/LIBRARY_PATH/operation=remove
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/append=true
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.1847537064/appendContributed=true
edge-addition-planarity-suite-Version_3.0.0.5/c/appconst.h 0000664 0000000 0000000 00000003771 12716155161 0023446 0 ustar 00root root 0000000 0000000 #ifndef APPCONST_H
#define APPCONST_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
// When PROFILE is defined, prints out run-time stats on a number of subordinate
// routines in the embedder
//#define PROFILE
#ifdef PROFILE
#include "platformTime.h"
#endif
/* Define DEBUG to get additional debugging. The default is to define it when MSC does */
#ifdef _DEBUG
#define DEBUG
#endif
/* Some low-level functions are replaced by faster macros, except when debugging */
#define SPEED_MACROS
#ifdef DEBUG
#undef SPEED_MACROS
#endif
/* Return status values; OK/NOTOK behave like Boolean true/false,
not like program exit codes. */
#define OK 1
#define NOTOK 0
#ifdef DEBUG
#undef NOTOK
extern int debugNOTOK();
#include
#define NOTOK (printf("NOTOK on Line %d of %s\n", __LINE__, __FILE__), debugNOTOK())
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0L
#endif
/* Array indices are used as pointers, and NIL means bad pointer */
// This definition is used with 1-based array indexing
#define NIL 0
#define NIL_CHAR 0x00
// This definition is used in combination with 0-based array indexing
//#define NIL -1
//#define NIL_CHAR 0xFF
/* Defines fopen strings for reading and writing text files on PC and UNIX */
#ifdef WINDOWS
#define READTEXT "rt"
#define WRITETEXT "wt"
#else
#define READTEXT "r"
#define WRITETEXT "w"
#endif
/********************************************************************
A few simple integer selection macros
********************************************************************/
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN3(x, y, z) MIN(MIN((x), (y)), MIN((y), (z)))
#define MAX3(x, y, z) MAX(MAX((x), (y)), MAX((y), (z)))
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graph.h 0000664 0000000 0000000 00000010202 12716155161 0022703 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_H
#define GRAPH_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "graphStructures.h"
#include "graphExtensions.h"
///////////////////////////////////////////////////////////////////////////////
// Definitions for higher-order operations at the vertex, edge and graph levels
///////////////////////////////////////////////////////////////////////////////
graphP gp_New(void);
int gp_InitGraph(graphP theGraph, int N);
void gp_ReinitializeGraph(graphP theGraph);
int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph);
int gp_CopyGraph(graphP dstGraph, graphP srcGraph);
graphP gp_DupGraph(graphP theGraph);
int gp_CreateRandomGraph(graphP theGraph);
int gp_CreateRandomGraphEx(graphP theGraph, int numEdges);
void gp_Free(graphP *pGraph);
int gp_Read(graphP theGraph, char *FileName);
#define WRITE_ADJLIST 1
#define WRITE_ADJMATRIX 2
#define WRITE_DEBUGINFO 3
int gp_Write(graphP theGraph, char *FileName, int Mode);
int gp_IsNeighbor(graphP theGraph, int u, int v);
int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v);
int gp_GetVertexDegree(graphP theGraph, int v);
int gp_GetVertexInDegree(graphP theGraph, int v);
int gp_GetVertexOutDegree(graphP theGraph, int v);
int gp_GetArcCapacity(graphP theGraph);
int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);
int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink);
int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink,
int v, int e_v, int e_vlink);
void gp_HideEdge(graphP theGraph, int e);
void gp_RestoreEdge(graphP theGraph, int e);
int gp_HideVertex(graphP theGraph, int vertex);
int gp_DeleteEdge(graphP theGraph, int e, int nextLink);
int gp_ContractEdge(graphP theGraph, int e);
int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore);
int gp_RestoreVertices(graphP theGraph);
int gp_CreateDFSTree(graphP theGraph);
int gp_SortVertices(graphP theGraph);
int gp_LowpointAndLeastAncestor(graphP theGraph);
int gp_PreprocessForEmbedding(graphP theGraph);
int gp_Embed(graphP theGraph, int embedFlags);
int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult);
/* Possible Flags for gp_Embed. The planar and outerplanar settings are supported
natively. The rest require extension modules. */
#define EMBEDFLAGS_PLANAR 1
#define EMBEDFLAGS_OUTERPLANAR 2
#define EMBEDFLAGS_DRAWPLANAR (4|EMBEDFLAGS_PLANAR)
#define EMBEDFLAGS_SEARCHFORK23 (16|EMBEDFLAGS_OUTERPLANAR)
#define EMBEDFLAGS_SEARCHFORK4 (32|EMBEDFLAGS_OUTERPLANAR)
#define EMBEDFLAGS_SEARCHFORK33 (64|EMBEDFLAGS_PLANAR)
#define EMBEDFLAGS_SEARCHFORK5 (128|EMBEDFLAGS_PLANAR)
#define EMBEDFLAGS_MAXIMALPLANARSUBGRAPH 256
#define EMBEDFLAGS_PROJECTIVEPLANAR 512
#define EMBEDFLAGS_TOROIDAL 1024
/* If LOGGING is defined, then write to the log, otherwise no-op
By default, neither release nor DEBUG builds including LOGGING.
Logging is useful for seeing details of how various algorithms
handle a particular graph. */
//#define LOGGING
#ifdef LOGGING
#define gp_LogLine _LogLine
#define gp_Log _Log
void _LogLine(char *Line);
void _Log(char *Line);
#define gp_MakeLogStr1 _MakeLogStr1
#define gp_MakeLogStr2 _MakeLogStr2
#define gp_MakeLogStr3 _MakeLogStr3
#define gp_MakeLogStr4 _MakeLogStr4
#define gp_MakeLogStr5 _MakeLogStr5
char *_MakeLogStr1(char *format, int);
char *_MakeLogStr2(char *format, int, int);
char *_MakeLogStr3(char *format, int, int, int);
char *_MakeLogStr4(char *format, int, int, int, int);
char *_MakeLogStr5(char *format, int, int, int, int, int);
#else
#define gp_LogLine(Line)
#define gp_Log(Line)
#define gp_MakeLogStr1(format, one)
#define gp_MakeLogStr2(format, one, two)
#define gp_MakeLogStr3(format, one, two, three)
#define gp_MakeLogStr4(format, one, two, three, four)
#define gp_MakeLogStr5(format, one, two, three, four, five)
#endif
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphDFSUtils.c 0000664 0000000 0000000 00000037555 12716155161 0024300 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#define GRAPHDFSUTILS_C
#include "graph.h"
extern void _ClearVertexVisitedFlags(graphP theGraph, int);
/********************************************************************
gp_CreateDFSTree
Assigns Depth First Index (DFI) to each vertex. Also records parent
of each vertex in the DFS tree, and marks DFS tree edges that go from
parent to child. Forward arc cycle edges are also distinguished from
edges leading from a DFS tree descendant to an ancestor-- both DFS tree
edges and back arcs. The forward arcs are moved to the end of the
adjacency list to make the set easier to find and process.
NOTE: This is a utility function provided for general use of the graph
library. The core planarity algorithm uses its own DFS in order to build
up related data structures at the same time as the DFS tree is created.
********************************************************************/
#include "platformTime.h"
int gp_CreateDFSTree(graphP theGraph)
{
stackP theStack;
int N, DFI, v, uparent, u, e;
#ifdef PROFILE
platform_time start, end;
platform_GetTime(start);
#endif
if (theGraph==NULL) return NOTOK;
if (theGraph->internalFlags & FLAGS_DFSNUMBERED) return OK;
gp_LogLine("\ngraphDFSUtils.c/gp_CreateDFSTree() start");
N = theGraph->N;
theStack = theGraph->theStack;
/* There are 2M edge records (arcs) and for each we can push 2 integers,
so a stack of 2 * arcCapacity integers suffices.
This is already in theGraph structure, so we make sure it's empty,
then clear all visited flags in prep for the Depth first search. */
if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph))
return NOTOK;
sp_ClearStack(theStack);
_ClearVertexVisitedFlags(theGraph, FALSE);
/* This outer loop causes the connected subgraphs of a disconnected
graph to be numbered */
for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++)
{
if (gp_IsNotDFSTreeRoot(theGraph, v))
continue;
sp_Push2(theStack, NIL, NIL);
while (sp_NonEmpty(theStack))
{
sp_Pop2(theStack, uparent, e);
u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e);
if (!gp_GetVertexVisited(theGraph, u))
{
gp_LogLine(gp_MakeLogStr3("V=%d, DFI=%d, Parent=%d", u, DFI, uparent));
gp_SetVertexVisited(theGraph, u);
gp_SetVertexIndex(theGraph, u, DFI++);
gp_SetVertexParent(theGraph, u, uparent);
if (gp_IsArc(e))
{
gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD);
gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT);
}
/* Push edges to all unvisited neighbors. These will be either
tree edges to children or forward arcs of back edges */
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)))
sp_Push2(theStack, u, e);
e = gp_GetNextArc(theGraph, e);
}
}
else
{
// If the edge leads to a visited vertex, then it is
// the forward arc of a back edge.
gp_SetEdgeType(theGraph, e, EDGE_TYPE_FORWARD);
gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_BACK);
}
}
}
gp_LogLine("graphDFSUtils.c/gp_CreateDFSTree() end\n");
theGraph->internalFlags |= FLAGS_DFSNUMBERED;
#ifdef PROFILE
platform_GetTime(end);
printf("DFS in %.3lf seconds.\n", platform_GetDuration(start,end));
#endif
return OK;
}
/********************************************************************
gp_SortVertices()
Once depth first numbering has been applied to the graph, the index
member of each vertex contains the DFI. This routine can reorder the
vertices in linear time so that they appear in ascending order by DFI.
Note that the index field is then used to store the original number
of the vertex. Therefore, a second call to this method will put the
vertices back to the original order and put the DFIs back into the
index fields of the vertices.
NOTE: This function is used by the core planarity algorithm, once its
custom DFS has assigned DFIs to the vertices. Once gp_Embed() has
finished creating an embedding or obstructing subgraph, this function
can be called to restore the original vertex numbering, if needed.
********************************************************************/
int gp_SortVertices(graphP theGraph)
{
return theGraph->functions.fpSortVertices(theGraph);
}
int _SortVertices(graphP theGraph)
{
int v, EsizeOccupied, e, srcPos, dstPos;
#ifdef PROFILE
platform_time start, end;
platform_GetTime(start);
#endif
if (theGraph == NULL) return NOTOK;
if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED))
if (gp_CreateDFSTree(theGraph) != OK)
return NOTOK;
gp_LogLine("\ngraphDFSUtils.c/_SortVertices() start");
/* Change labels of edges from v to DFI(v)-- or vice versa
Also, if any links go back to locations 0 to n-1, then they
need to be changed because we are reordering the vertices */
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2)
{
if (gp_EdgeInUse(theGraph, e))
{
gp_SetNeighbor(theGraph, e, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e)));
gp_SetNeighbor(theGraph, e+1, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e+1)));
}
}
/* Convert DFSParent from v to DFI(v) or vice versa */
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
if (gp_IsNotDFSTreeRoot(theGraph, v))
gp_SetVertexParent(theGraph, v, gp_GetVertexIndex(theGraph, gp_GetVertexParent(theGraph, v)));
/* Sort by 'v using constant time random access. Move each vertex to its
destination 'v', and store its source location in 'v'. */
/* First we clear the visitation flags. We need these to help mark
visited vertices because we change the 'v' field to be the source
location, so we cannot use index==v as a test for whether the
correct vertex is in location 'index'. */
_ClearVertexVisitedFlags(theGraph, FALSE);
/* We visit each vertex location, skipping those marked as visited since
we've already moved the correct vertex into that location. The
inner loop swaps the vertex at location v into the correct position,
given by the index of the vertex at location v. Then it marks that
location as visited, then sets its index to be the location from
whence we obtained the vertex record. */
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
srcPos = v;
while (!gp_GetVertexVisited(theGraph, v))
{
dstPos = gp_GetVertexIndex(theGraph, v);
gp_SwapVertexRec(theGraph, dstPos, theGraph, v);
gp_SwapVertexInfo(theGraph, dstPos, theGraph, v);
gp_SetVertexVisited(theGraph, dstPos);
gp_SetVertexIndex(theGraph, dstPos, srcPos);
srcPos = dstPos;
}
}
/* Invert the bit that records the sort order of the graph */
theGraph->internalFlags ^= FLAGS_SORTEDBYDFI;
gp_LogLine("graphDFSUtils.c/_SortVertices() end\n");
#ifdef PROFILE
platform_GetTime(end);
printf("SortVertices in %.3lf seconds.\n", platform_GetDuration(start,end));
#endif
return OK;
}
/********************************************************************
gp_LowpointAndLeastAncestor()
leastAncestor(v): min(v, ancestor neighbors of v, excluding parent)
Lowpoint(v): min(leastAncestor(v), Lowpoint of DFS children of v)
Lowpoint is computed via a post-order traversal of the DFS tree.
We push the root of the DFS tree, then we loop while the stack is not empty.
We pop a vertex; if it is not marked, then we are on our way down the DFS
tree, so we mark it and push it back on, followed by pushing its
DFS children. The next time we pop the node, all of its children
will have been popped, marked+children pushed, and popped again. On
the second pop of the vertex, we can therefore compute the lowpoint
values based on the childrens' lowpoints and the least ancestor from
among the edges in the vertex's adjacency list.
If they have not already been performed, gp_CreateDFSTree() and
gp_SortVertices() are invoked on the graph, and it is left in the
sorted state on completion of this method.
NOTE: This is a utility function provided for general use of the graph
library. The core planarity algorithm computes leastAncestor during its
initial DFS, and it computes the lowpoint of a vertex as it embeds the
tree edges to its children.
********************************************************************/
int gp_LowpointAndLeastAncestor(graphP theGraph)
{
stackP theStack = theGraph->theStack;
int v, u, uneighbor, e, L, leastAncestor;
if (theGraph == NULL) return NOTOK;
if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED))
if (gp_CreateDFSTree(theGraph) != OK)
return NOTOK;
if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI))
if (gp_SortVertices(theGraph) != OK)
return NOTOK;
#ifdef PROFILE
platform_time start, end;
platform_GetTime(start);
#endif
gp_LogLine("\ngraphDFSUtils.c/gp_LowpointAndLeastAncestor() start");
// A stack of size N suffices because at maximum every vertex is pushed only once
// However, since a larger stack is needed for the main DFS, this is mainly documentation
if (sp_GetCapacity(theStack) < theGraph->N)
return NOTOK;
sp_ClearStack(theStack);
_ClearVertexVisitedFlags(theGraph, FALSE);
// This outer loop causes the connected subgraphs of a disconnected graph to be processed
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);)
{
if (gp_GetVertexVisited(theGraph, v))
{
++v;
continue;
}
sp_Push(theStack, v);
while (sp_NonEmpty(theStack))
{
sp_Pop(theStack, u);
// If not visited, then we're on the pre-order visitation, so push u and its DFS children
if (!gp_GetVertexVisited(theGraph, u))
{
// Mark u as visited, then push it back on the stack
gp_SetVertexVisited(theGraph, u);
++v;
sp_Push(theStack, u);
// Push the DFS children of u
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
{
sp_Push(theStack, gp_GetNeighbor(theGraph, e));
}
e = gp_GetNextArc(theGraph, e);
}
}
// If u has been visited before, then this is the post-order visitation
else
{
// Start with high values because we are doing a min function
leastAncestor = L = u;
// Compute leastAncestor and L, the least lowpoint from the DFS children
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
uneighbor = gp_GetNeighbor(theGraph, e);
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
{
if (L > gp_GetVertexLowpoint(theGraph, uneighbor))
L = gp_GetVertexLowpoint(theGraph, uneighbor);
}
else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK)
{
if (leastAncestor > uneighbor)
leastAncestor = uneighbor;
}
e = gp_GetNextArc(theGraph, e);
}
/* Assign leastAncestor and Lowpoint to the vertex */
gp_SetVertexLeastAncestor(theGraph, u, leastAncestor);
gp_SetVertexLowpoint(theGraph, u, leastAncestor < L ? leastAncestor : L);
}
}
}
gp_LogLine("graphDFSUtils.c/gp_LowpointAndLeastAncestor() end\n");
#ifdef PROFILE
platform_GetTime(end);
printf("Lowpoint in %.3lf seconds.\n", platform_GetDuration(start,end));
#endif
return OK;
}
/********************************************************************
gp_LeastAncestor()
By simple pre-order visitation, compute the least ancestor of each
vertex that is directly adjacent to the vertex by a back edge.
If they have not already been performed, gp_CreateDFSTree() and
gp_SortVertices() are invoked on the graph, and it is left in the
sorted state on completion of this method.
NOTE: This method is not called by gp_LowpointAndLeastAncestor(),
which computes both values at the same time.
NOTE: This method is useful in core planarity initialization when
a graph has already been DFS numbered and sorted by DFI. For example,
this allows the core planarity embedder to avoid perturbing unit test
graphs that may be designed and stored in a DFI sorted format.
********************************************************************/
int gp_LeastAncestor(graphP theGraph)
{
stackP theStack = theGraph->theStack;
int v, u, uneighbor, e, leastAncestor;
if (theGraph == NULL) return NOTOK;
if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED))
if (gp_CreateDFSTree(theGraph) != OK)
return NOTOK;
if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI))
if (gp_SortVertices(theGraph) != OK)
return NOTOK;
#ifdef PROFILE
platform_time start, end;
platform_GetTime(start);
#endif
gp_LogLine("\ngraphDFSUtils.c/gp_LeastAncestor() start");
// A stack of size N suffices because at maximum every vertex is pushed only once
if (sp_GetCapacity(theStack) < theGraph->N)
return NOTOK;
sp_ClearStack(theStack);
// This outer loop causes the connected subgraphs of a disconnected graph to be processed
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);)
{
if (gp_GetVertexVisited(theGraph, v))
{
++v;
continue;
}
sp_Push(theStack, v);
while (sp_NonEmpty(theStack))
{
sp_Pop(theStack, u);
if (!gp_GetVertexVisited(theGraph, u))
{
gp_SetVertexVisited(theGraph, u);
++v;
leastAncestor = u;
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
uneighbor = gp_GetNeighbor(theGraph, e);
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
{
sp_Push(theStack, uneighbor);
}
else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK)
{
if (leastAncestor > uneighbor)
leastAncestor = uneighbor;
}
e = gp_GetNextArc(theGraph, e);
}
gp_SetVertexLeastAncestor(theGraph, u, leastAncestor);
}
}
}
gp_LogLine("graphDFSUtils.c/gp_LeastAncestor() end\n");
#ifdef PROFILE
platform_GetTime(end);
printf("LeastAncestor in %.3lf seconds.\n", platform_GetDuration(start,end));
#endif
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphDrawPlanar.c 0000664 0000000 0000000 00000120513 12716155161 0024661 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphDrawPlanar.h"
#include "graphDrawPlanar.private.h"
extern int DRAWPLANAR_ID;
#include "graph.h"
#include
#include
#include
extern void _ClearVisitedFlags(graphP theGraph);
/* Private functions exported to system */
void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink);
int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink);
int _ComputeVisibilityRepresentation(DrawPlanarContext *context);
int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context);
/* Private functions */
int _ComputeVertexPositions(DrawPlanarContext *context);
int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pIndex);
int _ComputeEdgePositions(DrawPlanarContext *context);
int _ComputeVertexRanges(DrawPlanarContext *context);
int _ComputeEdgeRanges(DrawPlanarContext *context);
/********************************************************************
_ComputeVisibilityRepresentation()
Compute vertex positions
Compute edge positions
Assign horizontal ranges of vertices
Assign vertical ranges of edges
********************************************************************/
int _ComputeVisibilityRepresentation(DrawPlanarContext *context)
{
if (sp_NonEmpty(context->theGraph->edgeHoles))
return NOTOK;
if (_ComputeVertexPositions(context) != OK)
return NOTOK;
if (_ComputeEdgePositions(context) != OK)
return NOTOK;
if (_ComputeVertexRanges(context) != OK)
return NOTOK;
if (_ComputeEdgeRanges(context) != OK)
return NOTOK;
return OK;
}
/********************************************************************
_ComputeVertexPositions()
Computes the vertex positions in the graph. This method accounts
for disconnected graphs by finding the DFS tree roots and then,
for each, invoking _ComputeVertexPositionsInComponent().
The index variable for the positioning is maintained by this method
so that the vertices in separate components still get distinct
vertex positions.
********************************************************************/
int _ComputeVertexPositions(DrawPlanarContext *context)
{
graphP theEmbedding = context->theGraph;
int v, vertpos;
vertpos = 0;
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
{
// For each DFS tree root in the embedding, we
// compute the vertex positions
if (gp_IsDFSTreeRoot(theEmbedding, v))
{
if (_ComputeVertexPositionsInComponent(context, v, &vertpos) != OK)
return NOTOK;
}
}
return OK;
}
/********************************************************************
_ComputeVertexPositionsInComponent()
The vertical positions of the vertices are computed based in part
on the information compiled during the planar embedding.
Each vertex is marked as being between its parent and some ancestor
or beyond the parent relative to the ancestor. The localized,
intuitive notion is that the vertex is either below the parent
or above the parent, but the bicomp containing the vertex, its
parent and the ancestor may be turned upside-down as the result
of a global sequence of operations, resulting in a between or beyond
generalization.
As the core planarity algorithm constructs successively larger
bicomps out of smaller ones, the bicomp root and its DFS child
are marked as 'tied' in vertex position using markers along the
external face. The marking of the DFS child may be indirect.
Since the child may not be on the external face, its descendant
that is next along the external face is marked instead.
Later (possibly in the same step or possibly many vertices later),
the Walkdown proceeds around the bicomp and returns to each merge
point, and the tie is broken based on the direction of approach.
As the Walkdown passes a vertex to its successor, the external
face is short-circuited to remove the vertex from it. Immediately
before this occurs, the new drawing method resolves the tie. Since
the vertex is going to the internal face, its vertex position should
be 'between' its successor and the current vertex being processed
by the Walkdown.
If the vertex is a child of its external face successor, then it
is simply marked as being 'between' that successor and the current
vertex being processed by the planarity method. But if the vertex
is the parent of its external face successor, then the successor
is placed 'beyond' the vertex. Recall that the successor is either
the DFS child of the vertex or a descendant of that DFS child that
was specially marked because it, not the DFS child, was on the
external face.
This explains the information that has been collected by the
planarity embedder, which will now be turned into a vertex ordering
system. The idea is to proceed with a pre-order traversal of
the DFS tree, determining the relative orders of the ancestors of
a vertex by the time we get to a vertex. This will allow us to
convert between/beyond into above/below based on the known relative
order of the parent and some given ancestor of the vertex. A vertex
would then be added immediately above or below its parent in the
total ordering, and then the algorithm proceeds to the descendants.
Consider a depth-first pre-order visitation of vertices. If the
full order of all vertices visited so far is dynamically maintained,
then it is easy to decide whether a vertex goes above or below
its parent based on the between/beyond indicator and the relative
positions in the order of the parent and given ancestor of the
vertex. If the ancestor is above the parent, then 'between' means
put the vertex immediately above its parent and 'beyond' means put
the vertex immediately below its parent in the order. And if the
ancestor is below the parent, then the meaning of between and
beyond are simply reversed.
Once a vertex is known to be above or below its parent, the drawing
flag is changed from between/beyond to above/below, and processing
proceeds to the next vertex in pre-order depth first search.
The difficulty lies in keeping an up-to-date topological ordering
that can be queried in constant time to find the relative positions
of two vertices. By itself, this is an instance of "online" or
dynamic topological sorting and has been proven not to be achievable
in linear total time. But this is a special case of the problem and
is therefore solvable through the collection and maintenance of some
additional information.
Recall that the ancestor V of a vertex is recorded when the setting
for between/beyond is made for a vertex. However, the Walkdown is
invoked on the bicomp rooted by edge (V', C), so the child C of V
that roots the subtree containing the vertex being marked is known.
Note that when a DFS child is placed above its parent, the entire
DFS subtree of vertices is placed above the parent. Hence, to
determine whether the parent P of a vertex W is above the ancestor
V, where W is marked either between or beyond P and V, we need
only determine the relationship between V and C, which has already
been directly determined due to previous steps of the algorithm
(because V and C are ancestors of P and W). If C is above/below V
then so is P.
As mentioned above, once the position of P is known relative to V,
it is a simple matter to decide whether to put W above or below P
based on the between/beyond indicator stored in W during embedding.
********************************************************************/
int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pVertpos)
{
graphP theEmbedding = context->theGraph;
listCollectionP theOrder = LCNew(gp_PrimaryVertexIndexBound(theEmbedding));
int W, P, C, V, e;
if (theOrder == NULL)
return NOTOK;
// Determine the vertex order using a depth first search with
// pre-order visitation.
sp_ClearStack(theEmbedding->theStack);
sp_Push(theEmbedding->theStack, root);
while (!sp_IsEmpty(theEmbedding->theStack))
{
sp_Pop(theEmbedding->theStack, W);
P = gp_GetVertexParent(theEmbedding, W);
V = context->VI[W].ancestor;
C = context->VI[W].ancestorChild;
// For the special case that we just popped the DFS tree root,
// we simply add the root to its own position.
if (gp_IsNotVertex(P))
{
// Put the DFS root in the list by itself
LCAppend(theOrder, NIL, W);
// The children of the DFS root have the root as their
// ancestorChild and 'beyond' as the drawingFlag, so this
// causes the root's children to be placed below the root
context->VI[W].drawingFlag = DRAWINGFLAG_BELOW;
}
// Determine vertex W position relative to P
else
{
// An unresolved tie is an error
if (context->VI[W].drawingFlag == DRAWINGFLAG_TIE)
return NOTOK;
// If W is the child of a DFS root, then there is no vertex C
// between it and some ancestor V. Both V and C are not a vertex,
// and W will simply take the default of being below its parent.
// If C is a vertex, then it has already been absolutely positioned
// and can be used to help position W relative to its parent P,
// which is equal to or descendant to C. If C below V, then P below V,
// so interpret 'W between P and V' as 'W above P', and interpret
// 'W beyond P relative to V' as 'W below P'.
if (gp_IsNotVertex(C) || context->VI[C].drawingFlag == DRAWINGFLAG_BELOW)
{
if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN)
context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE;
else
context->VI[W].drawingFlag = DRAWINGFLAG_BELOW;
}
// If C above V, then P above V, so interpret W between
// P and V as W below P, and interpret W beyond P relative
// to V as W above P.
else
{
if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN)
context->VI[W].drawingFlag = DRAWINGFLAG_BELOW;
else
context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE;
}
if (context->VI[W].drawingFlag == DRAWINGFLAG_BELOW)
LCInsertAfter(theOrder, P, W);
else
LCInsertBefore(theOrder, P, W);
}
// Push DFS children
e = gp_GetFirstArc(theEmbedding, W);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theEmbedding, e) == EDGE_TYPE_CHILD)
sp_Push(theEmbedding->theStack, gp_GetNeighbor(theEmbedding, e));
e = gp_GetNextArc(theEmbedding, e);
}
}
// Use the order to assign vertical positions
V = root;
while (gp_IsVertex(V))
{
context->VI[V].pos = *pVertpos;
(*pVertpos)++;
V = LCGetNext(theOrder, root, V);
}
// Clean up and return
LCFree(&theOrder);
return OK;
}
#ifdef LOGGING
/********************************************************************
_LogEdgeList()
Used to show the progressive calculation of the edge position list.
********************************************************************/
void _LogEdgeList(graphP theEmbedding, listCollectionP edgeList, int edgeListHead)
{
int eIndex = edgeListHead, e, eTwin;
gp_Log("EdgeList: [ ");
while (gp_IsArc(eIndex))
{
e = (eIndex << 1);
eTwin = gp_GetTwinArc(theEmbedding, e);
gp_Log(gp_MakeLogStr2("(%d, %d) ",
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)),
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eTwin))));
eIndex = LCGetNext(edgeList, edgeListHead, eIndex);
}
gp_LogLine("]");
}
#endif
/********************************************************************
_ComputeEdgePositions()
Performs a vertical sweep of the combinatorial planar embedding,
developing the edge order in the horizontal sweep line as it
advances through the vertices according to their assigned
vertical positions.
The 'visitedInfo' member of each vertex is used to indicate the
location in the edge order list of the generator edge for the vertex.
The generator edge is the first edge used to visit the vertex from
a higher vertex in the drawing (i.e. a vertex with an earlier, or
lower, position number).
All edges added from this vertex to the neighbors below it are
added immediately after the generator edge for the vertex.
********************************************************************/
int _ComputeEdgePositions(DrawPlanarContext *context)
{
graphP theEmbedding = context->theGraph;
int *vertexOrder = NULL;
listCollectionP edgeList = NULL;
int edgeListHead, edgeListInsertPoint;
int e, eTwin, eCur, v, vpos, epos, eIndex;
gp_LogLine("\ngraphDrawPlanar.c/_ComputeEdgePositions() start");
// Sort the vertices by vertical position (in linear time)
if ((vertexOrder = (int *) malloc(theEmbedding->N * sizeof(int))) == NULL)
return NOTOK;
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
vertexOrder[context->VI[v].pos] = v;
// Allocate the edge list of size M.
// This is an array of (prev, next) pointers.
// An edge at position X corresponds to the edge
// at position X in the graph structure, which is
// represented by a pair of adjacent edge records
// at index 2X.
if (theEmbedding->M > 0 && (edgeList = LCNew(gp_GetFirstEdge(theEmbedding)/2+theEmbedding->M)) == NULL)
{
free(vertexOrder);
return NOTOK;
}
edgeListHead = NIL;
// Each vertex starts out with a NIL generator edge.
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
gp_SetVertexVisitedInfo(theEmbedding, v, NIL);
// Perform the vertical sweep of the combinatorial embedding, using
// the vertex ordering to guide the sweep.
// For each vertex, each edge leading to a vertex with a higher number in
// the vertex order is recorded as the "generator edge", or the edge of
// first discovery of that higher numbered vertex, unless the vertex already has
// a recorded generator edge
for (vpos = 0; vpos < theEmbedding->N; vpos++)
{
// Get the vertex associated with the position
v = vertexOrder[vpos];
gp_LogLine(gp_MakeLogStr3("Processing vertex %d with DFI=%d at position=%d",
gp_GetVertexIndex(theEmbedding, v), v, vpos));
// The DFS tree root of a connected component is always the least
// number vertex in the vertex ordering. We have to give it a
// false generator edge so that it is still "visited" and then
// all of its edges are generators for its neighbor vertices because
// they all have greater numbers in the vertex order.
if (gp_IsDFSTreeRoot(theEmbedding, v))
{
// Set a false generator edge, so the vertex is distinguishable from
// a vertex with no generator edge when its neighbors are visited
// This way, an edge from a neighbor won't get recorded as the
// generator edge of the DFS tree root.
gp_SetVertexVisitedInfo(theEmbedding, v, NIL - 1);
// Now we traverse the adjacency list of the DFS tree root and
// record each edge as the generator edge of the neighbors
e = gp_GetFirstArc(theEmbedding, v);
while (gp_IsArc(e))
{
eIndex = (e >> 1); // div by 2 since each edge is a pair of arcs
edgeListHead = LCAppend(edgeList, edgeListHead, eIndex);
gp_LogLine(gp_MakeLogStr2("Append generator edge (%d, %d) to edgeList",
gp_GetVertexIndex(theEmbedding, v), gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e))));
// Set the generator edge for the root's neighbor
gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, e), e);
// Go to the next node of the root's adj list
e = gp_GetNextArc(theEmbedding, e);
}
}
// Else, if we are not on a DFS tree root...
else
{
// Get the generator edge of the vertex
// Note that this never gets the false generator edge of a DFS tree root
eTwin = gp_GetVertexVisitedInfo(theEmbedding, v);
if (gp_IsNotArc(eTwin))
return NOTOK;
e = gp_GetTwinArc(theEmbedding, eTwin);
// Traverse the edges of the vertex, starting
// from the generator edge and going counterclockwise...
eIndex = (e >> 1);
edgeListInsertPoint = eIndex;
eCur = gp_GetNextArcCircular(theEmbedding, e);
while (eCur != e)
{
// If the neighboring vertex's position is greater
// than the current vertex (meaning it is lower in the
// diagram), then add that edge to the edge order.
if (context->VI[gp_GetNeighbor(theEmbedding, eCur)].pos > vpos)
{
eIndex = eCur >> 1;
LCInsertAfter(edgeList, edgeListInsertPoint, eIndex);
gp_LogLine(gp_MakeLogStr4("Insert (%d, %d) after (%d, %d)",
gp_GetVertexIndex(theEmbedding, v),
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)),
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))),
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e))));
edgeListInsertPoint = eIndex;
// If the vertex does not yet have a generator edge, then set it.
// Note that a DFS tree root has a false generator edge, so this if
// test avoids setting a generator edge for a DFS tree root
if (gp_IsNotArc(gp_GetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur))))
{
gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur), eCur);
gp_LogLine(gp_MakeLogStr2("Generator edge (%d, %d)",
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))),
gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur))));
}
}
// Go to the next node in v's adjacency list
eCur = gp_GetNextArcCircular(theEmbedding, eCur);
}
}
#ifdef LOGGING
_LogEdgeList(theEmbedding, edgeList, edgeListHead);
#endif
}
// Now iterate through the edgeList and assign positions to the edges.
epos = 0;
eIndex = edgeListHead;
while (gp_IsArc(eIndex))
{
e = (eIndex << 1);
eTwin = gp_GetTwinArc(theEmbedding, e);
context->E[e].pos = context->E[eTwin].pos = epos;
epos++;
eIndex = LCGetNext(edgeList, edgeListHead, eIndex);
}
// Clean up and return
LCFree(&edgeList);
free(vertexOrder);
gp_LogLine("graphDrawPlanar.c/_ComputeEdgePositions() end\n");
return OK;
}
/********************************************************************
_ComputeVertexRanges()
Assumes edge positions are known (see _ComputeEdgePositions()).
A vertex spans horizontally the positions of the edges incident
to it.
********************************************************************/
int _ComputeVertexRanges(DrawPlanarContext *context)
{
graphP theEmbedding = context->theGraph;
int v, e, min, max;
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
{
min = theEmbedding->M + 1;
max = -1;
// Iterate the edges, except in the isolated vertex case we just
// set the min and max to 1 since there no edges controlling where
// it gets drawn.
e = gp_GetFirstArc(theEmbedding, v);
if (gp_IsNotArc(e))
{
min = max = 0;
}
else
{
while (gp_IsArc(e))
{
if (min > context->E[e].pos)
min = context->E[e].pos;
if (max < context->E[e].pos)
max = context->E[e].pos;
e = gp_GetNextArc(theEmbedding, e);
}
}
context->VI[v].start = min;
context->VI[v].end = max;
}
return OK;
}
/********************************************************************
_ComputeEdgeRanges()
Assumes vertex positions are known (see _ComputeVertexPositions()).
An edges spans the vertical range of its endpoints.
********************************************************************/
int _ComputeEdgeRanges(DrawPlanarContext *context)
{
graphP theEmbedding = context->theGraph;
int e, eTwin, EsizeOccupied, v1, v2, pos1, pos2;
// Deleted edges are not supported, nor should they be in the embedding, so
// this is just a reality check that avoids an in-use test inside the loop
if (sp_NonEmpty(theEmbedding->edgeHoles))
return NOTOK;
EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding);
for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2)
{
eTwin = gp_GetTwinArc(theEmbedding, e);
v1 = gp_GetNeighbor(theEmbedding, e);
v2 = gp_GetNeighbor(theEmbedding, eTwin);
pos1 = context->VI[v1].pos;
pos2 = context->VI[v2].pos;
if (pos1 < pos2)
{
context->E[e].start = pos1;
context->E[e].end = pos2;
}
else
{
context->E[e].start = pos2;
context->E[e].end = pos1;
}
context->E[eTwin].start = context->E[e].start;
context->E[eTwin].end = context->E[e].end;
}
return OK;
}
/********************************************************************
_GetNextExternalFaceVertex()
Uses the extFace links to traverse to the next vertex on the external
face given a current vertex and the link that points to its predecessor.
********************************************************************/
int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink)
{
int nextVertex = gp_GetExtFaceVertex(theGraph, curVertex, 1 ^ *pPrevLink);
// If the two links in the new vertex are not equal, then only one points
// back to the current vertex, and it is the new prev link.
// Otherwise, the vertex is in a consistently oriented single-edge bicomp, so
// no adjustment of the prev link is needed (due to the consistent orientation).
if (gp_GetExtFaceVertex(theGraph, nextVertex, 0) != gp_GetExtFaceVertex(theGraph, nextVertex, 1))
{
*pPrevLink = gp_GetExtFaceVertex(theGraph, nextVertex, 0)==curVertex ? 0 : 1;
}
return nextVertex;
}
/********************************************************************
_CollectDrawingData()
To be called by core planarity Walkdown immediately before merging
bicomps and embedding a new back edge.
Each bicomp is rooted by a DFS tree edge. The parent vertex in
that edge is the bicomp root, and the bicomp contains one DFS child
of the vertex, which is on the child end of the 'root edge'.
Here we decide whether the DFS child is to be embedded between or
beyond its parent relative to vertex v, the one currently being
processed (and the ancestor endpoint of a back edge being embedded,
where the descendant endpoint is also an endpoint of the bicomp
root being merged).
********************************************************************/
void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink)
{
graphP theEmbedding = context->theGraph;
int K, Parent, BicompRoot, DFSChild, direction, descendant;
gp_LogLine("\ngraphDrawPlanar.c/_CollectDrawingData() start");
gp_LogLine(gp_MakeLogStr3("_CollectDrawingData(RootVertex=%d, W=%d, W_in=%d)",
RootVertex, W, WPrevLink));
/* Process all of the merge points to set their drawing flags. */
for (K = 0; K < sp_GetCurrentSize(theEmbedding->theStack); K += 4)
{
/* Get the parent and child that are about to be merged from
the 4-tuple in the merge stack */
Parent = theEmbedding->theStack->S[K];
BicompRoot = theEmbedding->theStack->S[K+2];
DFSChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot);
/* We get the active descendant vertex in the child bicomp that
will be adjacent to the parent along the external face.
This vertex is guaranteed to be found in one step
due to external face 'short-circuiting' that was done in
step 'Parent' of the planarity algorithm.
We pass theEmbedding->N for the second parameter because
of this; we use this function to signify need of extFace
links in the other implementation.*/
direction = theEmbedding->theStack->S[K+3];
descendant = _GetNextExternalFaceVertex(theEmbedding, BicompRoot, &direction);
/* Now we set the tie flag in the DFS child, and mark the
descendant and parent with non-NIL pointers to the child
whose tie flag is to be resolved as soon as one of the
two is connected to by an edge or child bicomp merge. */
context->VI[DFSChild].drawingFlag = DRAWINGFLAG_TIE;
context->VI[descendant].tie[direction] = DFSChild;
direction = theEmbedding->theStack->S[K+1];
context->VI[Parent].tie[direction] = DFSChild;
gp_LogLine(gp_MakeLogStr5("V[Parent=%d]=.tie[%d] = V[descendant=%d].tie[%d] = (child=%d)",
Parent, direction, descendant, theEmbedding->theStack->S[K+3], DFSChild));
}
gp_LogLine("graphDrawPlanar.c/_CollectDrawingData() end\n");
}
/********************************************************************
_BreakTie()
The given vertex W has just been arrived at by the core planarity
algorithm. Using WPrevLink, we seek its predecessor WPred on the
external face and test whether the two are involved in a tie that
can be resolved.
Since the planarity algorithm has just passed by WPred, it is
safe to conclude that WPred can go between W and the current vertex.
Of course, if W was the parent to some DFS child whose subtree
contains WPred, then the DFS child is marked 'between', placing
the whole subtree including WPred between W and the current vertex.
On the other hand, if WPred was the parent of some DFS child whose
subtree contained W, then we achieve the same effect of putting WPred
'between' W and the curent vertex by marking the DFS child 'beyond'.
Since the DFS child and hence W are beyond W relative to the current
vertex, WPred is also between W and the current vertex.
Thus the certain positional relationship between W and WPred
relative to a specific ancestor, the current vertex, is used to
indirectly break the positional tie between MIN(W, WPred) and the
DFS child of MIN(W, WPred) whose subtree contains MAX(W, WPred).
The ancestorChild is the DFS child of the current vertex whose DFS
subtree contains W and WPred, and it is recorded here in order to
optimize the post-processing calculation of vertex positions.
********************************************************************/
int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink)
{
graphP theEmbedding = context->theGraph;
/* First we get the predecessor of W. */
int WPredNextLink = 1^WPrevLink,
WPred = _GetNextExternalFaceVertex(theEmbedding, W, &WPredNextLink);
gp_LogLine("\ngraphDrawPlanar.c/::_BreakTie() start");
gp_LogLine(gp_MakeLogStr4("_BreakTie(BicompRoot=%d, W=%d, W_in=%d) WPred=%d",
BicompRoot, W, WPrevLink, WPred));
/* Ties happen only within a bicomp (i.e. between two non-root vertices) */
if (gp_IsVirtualVertex(theEmbedding, W) || gp_IsVirtualVertex(theEmbedding, WPred))
{
gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n");
return OK;
}
/* The two vertices are either tied or not; having one tied and the other
not is an error */
if (context->VI[W].tie[WPrevLink] != context->VI[WPred].tie[WPredNextLink])
return NOTOK;
/* If there is a tie, it can now be resolved. */
if (gp_IsVertex(context->VI[W].tie[WPrevLink]))
{
int DFSChild = context->VI[W].tie[WPrevLink];
/* Set the two ancestor variables that contextualize putting W 'between'
or 'beyond' its parent relative to what. */
context->VI[DFSChild].ancestorChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot);
context->VI[DFSChild].ancestor = gp_GetPrimaryVertexFromRoot(theEmbedding, BicompRoot);
gp_LogLine(gp_MakeLogStr4("V[child=%d]=.ancestorChild = %d, V[child=%d]=.ancestor = %d",
DFSChild, context->VI[DFSChild].ancestorChild, DFSChild, context->VI[DFSChild].ancestor));
/* If W is the ancestor of WPred, then the DFSChild subtree contains
WPred, and so must go between W and some ancestor. */
if (W < WPred)
{
context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BETWEEN;
gp_LogLine(gp_MakeLogStr3("Child=%d is 'between' ancestorChild=%d and ancestor=%d",
DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor));
}
/* If W is the descendant, so we achieve the effect of putting WPred
between DFSChild and ancestor by putting the DFSChild 'beyond' WPred. */
else
{
context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BEYOND;
gp_LogLine(gp_MakeLogStr3("Child=%d is 'beyond' ancestorChild=%d relative to ancestor=%d",
DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor));
}
/* The tie is resolved so clear the flags*/
context->VI[W].tie[WPrevLink] = NIL;
context->VI[WPred].tie[WPredNextLink] = NIL;
}
gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n");
return OK;
}
/********************************************************************
_RenderToString()
Draws the previously calculated visibility representation in a
string of size (M+1)*2N + 1 characters, which should be deallocated
with free().
Returns NULL on failure, or the string containing the visibility
representation otherwise. The string can be printed using %s,
********************************************************************/
char *_RenderToString(graphP theEmbedding)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theEmbedding, DRAWPLANAR_ID, (void *) &context);
if (context != NULL)
{
int N = theEmbedding->N;
int M = theEmbedding->M;
int zeroBasedVertexOffset = (theEmbedding->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theEmbedding) : 0;
int n, m, EsizeOccupied, v, vRange, e, eRange, Mid, Pos;
char *visRep = (char *) malloc(sizeof(char) * ((M+1) * 2*N + 1));
char numBuffer[32];
if (visRep == NULL)
return NULL;
if (sp_NonEmpty(context->theGraph->edgeHoles))
{
free(visRep);
return NULL;
}
// Clear the space
for (n = 0; n < N; n++)
{
for (m=0; m < M; m++)
{
visRep[(2*n) * (M+1) + m] = ' ';
visRep[(2*n+1) * (M+1) + m] = ' ';
}
visRep[(2*n) * (M+1) + M] = '\n';
visRep[(2*n+1) * (M+1) + M] = '\n';
}
// Draw the vertices
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
{
Pos = context->VI[v].pos;
for (vRange=context->VI[v].start; vRange <= context->VI[v].end; vRange++)
visRep[(2*Pos) * (M+1) + vRange] = '-';
// Draw vertex label
Mid = (context->VI[v].start + context->VI[v].end) / 2;
sprintf(numBuffer, "%d", v - zeroBasedVertexOffset);
if ((unsigned)(context->VI[v].end - context->VI[v].start + 1) >= strlen(numBuffer))
{
strncpy(visRep + (2*Pos) * (M+1) + Mid, numBuffer, strlen(numBuffer));
}
// If the vertex width is less than the label width, then fail gracefully
else
{
if (strlen(numBuffer)==2)
visRep[(2*Pos) * (M+1) + Mid] = numBuffer[0];
else
visRep[(2*Pos) * (M+1) + Mid] = '*';
visRep[(2*Pos+1) * (M+1) + Mid] = numBuffer[strlen(numBuffer)-1];
}
}
// Draw the edges
EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding);
for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2)
{
Pos = context->E[e].pos;
for (eRange=context->E[e].start; eRange < context->E[e].end; eRange++)
{
if (eRange > context->E[e].start)
visRep[(2*eRange) * (M+1) + Pos] = '|';
visRep[(2*eRange+1) * (M+1) + Pos] = '|';
}
}
// Null terminate string and return it
visRep[(M+1) * 2*N] = '\0';
return visRep;
}
return NULL;
}
/********************************************************************
gp_DrawPlanar_RenderToFile()
Creates a rendition of the planar graph visibility representation
as a string, then dumps the string to the file.
********************************************************************/
int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName)
{
if (sp_IsEmpty(theEmbedding->edgeHoles))
{
FILE *outfile;
char *theRendition;
if (strcmp(theFileName, "stdout") == 0)
outfile = stdout;
else if (strcmp(theFileName, "stderr") == 0)
outfile = stderr;
else outfile = fopen(theFileName, WRITETEXT);
if (outfile == NULL)
return NOTOK;
theRendition = _RenderToString(theEmbedding);
if (theRendition != NULL)
{
fprintf(outfile, "%s", theRendition);
free(theRendition);
}
if (strcmp(theFileName, "stdout") == 0 || strcmp(theFileName, "stderr") == 0)
fflush(outfile);
else if (fclose(outfile) != 0)
return NOTOK;
return theRendition ? OK : NOTOK;
}
return NOTOK;
}
/********************************************************************
_CheckVisibilityRepresentationIntegrity()
********************************************************************/
int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context)
{
graphP theEmbedding = context->theGraph;
int v, e, eTwin, EsizeOccupied, epos, eposIndex;
if (sp_NonEmpty(context->theGraph->edgeHoles))
return NOTOK;
_ClearVisitedFlags(theEmbedding);
/* Test whether the vertex values make sense and
whether the vertex positions are unique. */
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
{
if (theEmbedding->M > 0)
{
if (context->VI[v].pos < 0 ||
context->VI[v].pos >= theEmbedding->N ||
context->VI[v].start < 0 ||
context->VI[v].start > context->VI[v].end ||
context->VI[v].end >= theEmbedding->M)
return NOTOK;
}
// Has the vertex position been used by a vertex before vertex v?
if (gp_GetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding)))
return NOTOK;
// Mark the vertex position as used by vertex v.
// Note that this marking is made on some other vertex unrelated to v
// We're just reusing the vertex visited array as cheap storage for a
// detector of reusing vertex position integers.
gp_SetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding));
}
/* Test whether the edge values make sense and
whether the edge positions are unique */
EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding);
for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2)
{
/* Each edge has two index locations in the edge information array */
eTwin = gp_GetTwinArc(theEmbedding, e);
if (context->E[e].pos != context->E[eTwin].pos ||
context->E[e].start != context->E[eTwin].start ||
context->E[e].end != context->E[eTwin].end ||
context->E[e].pos < 0 ||
context->E[e].pos >= theEmbedding->M ||
context->E[e].start < 0 ||
context->E[e].start > context->E[e].end ||
context->E[e].end >= theEmbedding->N)
return NOTOK;
/* Get the recorded horizontal position of that edge,
a number between 0 and M-1 */
epos = context->E[e].pos;
/* Convert that to an index in the graph structure so we
can use the visited flags in the graph's edges to
tell us whether the positions are being reused. */
eposIndex = (epos<<1) + gp_GetFirstEdge(theEmbedding);
eTwin = gp_GetTwinArc(theEmbedding, eposIndex);
if (gp_GetEdgeVisited(theEmbedding, eposIndex) || gp_GetEdgeVisited(theEmbedding, eTwin))
return NOTOK;
gp_SetEdgeVisited(theEmbedding, eposIndex);
gp_SetEdgeVisited(theEmbedding, eTwin);
}
/* Test whether any edge intersects any vertex position
for a vertex that is not an endpoint of the edge. */
EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding);
for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2)
{
eTwin = gp_GetTwinArc(theEmbedding, e);
for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++)
{
/* If the vertex is an endpoint of the edge, then... */
if (gp_GetNeighbor(theEmbedding, e) == v || gp_GetNeighbor(theEmbedding, eTwin) == v)
{
/* The vertical position of the vertex must be
at the top or bottom of the edge, */
if (context->E[e].start != context->VI[v].pos &&
context->E[e].end != context->VI[v].pos)
return NOTOK;
/* The horizontal edge position must be in the range of the vertex */
if (context->E[e].pos < context->VI[v].start ||
context->E[e].pos > context->VI[v].end)
return NOTOK;
}
/* If the vertex is not an endpoint of the edge... */
else // if (gp_GetNeighbor(theEmbedding, e) != v && gp_GetNeighbor(theEmbedding, eTwin) != v)
{
/* If the vertical position of the vertex is in the
vertical range of the edge ... */
if (context->E[e].start <= context->VI[v].pos &&
context->E[e].end >= context->VI[v].pos)
{
/* And if the horizontal position of the edge is in the
horizontal range of the vertex, then return an error. */
if (context->VI[v].start <= context->E[e].pos &&
context->VI[v].end >= context->E[e].pos)
return NOTOK;
}
}
}
}
/* All tests passed */
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphDrawPlanar.h 0000664 0000000 0000000 00000000772 12716155161 0024672 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_DRAWPLANAR_H
#define GRAPH_DRAWPLANAR_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphStructures.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DRAWPLANAR_NAME "DrawPlanar"
int gp_AttachDrawPlanar(graphP theGraph);
int gp_DetachDrawPlanar(graphP theGraph);
int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName);
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphDrawPlanar.private.h 0000664 0000000 0000000 00000005672 12716155161 0026347 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_DRAWPLANAR_PRIVATE_H
#define GRAPH_DRAWPLANAR_PRIVATE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graph.h"
#ifdef __cplusplus
extern "C" {
#endif
// Additional equipment for each edge
/*
pos, start, end: used to store a visibility representation, or
horvert diagram of a planar graph.
For edges, horizontal position, vertical range
*/
typedef struct
{
int pos, start, end;
} DrawPlanar_EdgeRec;
typedef DrawPlanar_EdgeRec * DrawPlanar_EdgeRecP;
// Additional equipment for each vertex
/*
pos, start, end: used to store a visibility representation, or
horvert diagram, of a planar graph.
For vertices, vertical position, horizontal range
drawingFlag, ancestor, ancestorChild: used to collect information needed
to help 'draw' a visibility representation. During planar
embedding, a vertex is determined to be between its DFS parent and
a given ancestor (the vertex being processed) or beyond the parent
relative to the ancestor. In post processing, the relative
orientation of the parent and ancestor are determined,
then the notion of between/beyond resolves to above/below or
below/above depending on whether the ancestor is above or below,
respectively, the parent. The ancestorChild are used to help r
esolve this latter question.
tie[2] stores information along the external face during embedding
that is pertinent to helping break ties in the decisions about
vertical vertex positioning. When vertices are first merged
together into a bicomp, we cannot always decide right away which
vertices will be above or below others. But as we traverse the
external face removing inactive vertices, these positional ties
can be resolved.
*/
typedef struct
{
int pos, start, end;
int drawingFlag, ancestor, ancestorChild;
int tie[2];
} DrawPlanar_VertexInfo;
typedef DrawPlanar_VertexInfo * DrawPlanar_VertexInfoP;
#define DRAWINGFLAG_BEYOND 0
#define DRAWINGFLAG_TIE 1
#define DRAWINGFLAG_BETWEEN 2
#define DRAWINGFLAG_BELOW 3
#define DRAWINGFLAG_ABOVE 4
typedef struct
{
// Helps distinguish initialize from re-initialize
int initialized;
// The graph that this context augments
graphP theGraph;
// Parallel array for additional edge level equipment
DrawPlanar_EdgeRecP E;
// Parallel array for additional vertex level equipment
DrawPlanar_VertexInfoP VI;
// Overloaded function pointers
graphFunctionTable functions;
} DrawPlanarContext;
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphDrawPlanar_Extensions.c 0000664 0000000 0000000 00000060672 12716155161 0027111 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "graphDrawPlanar.private.h"
#include "graphDrawPlanar.h"
extern void _ClearVertexVisitedFlags(graphP theGraph, int);
extern void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink);
extern int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink);
extern int _ComputeVisibilityRepresentation(DrawPlanarContext *context);
extern int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context);
/* Forward declarations of local functions */
void _DrawPlanar_ClearStructures(DrawPlanarContext *context);
int _DrawPlanar_CreateStructures(DrawPlanarContext *context);
int _DrawPlanar_InitStructures(DrawPlanarContext *context);
void _DrawPlanar_InitEdgeRec(DrawPlanarContext *context, int v);
void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v);
/* Forward declarations of overloading functions */
int _DrawPlanar_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink);
int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink);
int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
int _DrawPlanar_InitGraph(graphP theGraph, int N);
void _DrawPlanar_ReinitializeGraph(graphP theGraph);
int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);
int _DrawPlanar_SortVertices(graphP theGraph);
int _DrawPlanar_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize);
int _DrawPlanar_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize);
/* Forward declarations of functions used by the extension system */
void *_DrawPlanar_DupContext(void *pContext, void *theGraph);
void _DrawPlanar_FreeContext(void *);
/****************************************************************************
* DRAWPLANAR_ID - the variable used to hold the integer identifier for this
* extension, enabling this feature's extension context to be distinguished
* from other features' extension contexts that may be attached to a graph.
****************************************************************************/
int DRAWPLANAR_ID = 0;
/****************************************************************************
gp_AttachDrawPlanar()
This function adjusts the graph data structure to attach the planar graph
drawing feature.
To activate this feature during gp_Embed(), use EMBEDFLAGS_DRAWPLANAR.
This method may be called immediately after gp_New() in the case of
invoking gp_Read(). For generating graphs, gp_InitGraph() can be invoked
before or after this enabling method. This method detects if the core
graph has already been initialized, and if so, it will initialize the
additional data structures specific to planar graph drawing. This makes
it possible to invoke gp_New() and gp_InitGraph() together, and then attach
this feature only if it is requested at run-time.
Returns OK for success, NOTOK for failure.
****************************************************************************/
int gp_AttachDrawPlanar(graphP theGraph)
{
DrawPlanarContext *context = NULL;
// If the drawing feature has already been attached to the graph,
// then there is no need to attach it again
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
return OK;
}
// Allocate a new extension context
context = (DrawPlanarContext *) malloc(sizeof(DrawPlanarContext));
if (context == NULL)
{
return NOTOK;
}
// First, tell the context that it is not initialized
context->initialized = 0;
// Save a pointer to theGraph in the context
context->theGraph = theGraph;
// Put the overload functions into the context function table.
// gp_AddExtension will overload the graph's functions with these, and
// return the base function pointers in the context function table
memset(&context->functions, 0, sizeof(graphFunctionTable));
context->functions.fpMergeBicomps = _DrawPlanar_MergeBicomps;
context->functions.fpHandleInactiveVertex = _DrawPlanar_HandleInactiveVertex;
context->functions.fpEmbedPostprocess = _DrawPlanar_EmbedPostprocess;
context->functions.fpCheckEmbeddingIntegrity = _DrawPlanar_CheckEmbeddingIntegrity;
context->functions.fpCheckObstructionIntegrity = _DrawPlanar_CheckObstructionIntegrity;
context->functions.fpInitGraph = _DrawPlanar_InitGraph;
context->functions.fpReinitializeGraph = _DrawPlanar_ReinitializeGraph;
context->functions.fpEnsureArcCapacity = _DrawPlanar_EnsureArcCapacity;
context->functions.fpSortVertices = _DrawPlanar_SortVertices;
context->functions.fpReadPostprocess = _DrawPlanar_ReadPostprocess;
context->functions.fpWritePostprocess = _DrawPlanar_WritePostprocess;
_DrawPlanar_ClearStructures(context);
// Store the Draw context, including the data structure and the
// function pointers, as an extension of the graph
if (gp_AddExtension(theGraph, &DRAWPLANAR_ID, (void *) context,
_DrawPlanar_DupContext, _DrawPlanar_FreeContext,
&context->functions) != OK)
{
_DrawPlanar_FreeContext(context);
return NOTOK;
}
// Create the Draw-specific structures if the size of the graph is known
// Attach functions are typically invoked after gp_New(), but if a graph
// extension must be attached before gp_Read(), then the attachment
// also happens before gp_InitGraph() because gp_Read() invokes init only
// after it reads the order N of the graph. Hence, this attach call would
// occur when N==0 in the case of gp_Read().
// But if a feature is attached after gp_InitGraph(), then N > 0 and so we
// need to create and initialize all the custom data structures
if (theGraph->N > 0)
{
if (_DrawPlanar_CreateStructures(context) != OK ||
_DrawPlanar_InitStructures(context) != OK)
{
_DrawPlanar_FreeContext(context);
return NOTOK;
}
}
return OK;
}
/********************************************************************
gp_DetachDrawPlanar()
********************************************************************/
int gp_DetachDrawPlanar(graphP theGraph)
{
return gp_RemoveExtension(theGraph, DRAWPLANAR_ID);
}
/********************************************************************
_DrawPlanar_ClearStructures()
********************************************************************/
void _DrawPlanar_ClearStructures(DrawPlanarContext *context)
{
if (!context->initialized)
{
// Before initialization, the pointers are stray, not NULL
// Once NULL or allocated, free() or LCFree() can do the job
context->E = NULL;
context->VI = NULL;
context->initialized = 1;
}
else
{
if (context->E != NULL)
{
free(context->E);
context->E = NULL;
}
if (context->VI != NULL)
{
free(context->VI);
context->VI = NULL;
}
}
}
/********************************************************************
_DrawPlanar_CreateStructures()
Create uninitialized structures for the vertex and edge levels,
and initialized structures for the graph level
********************************************************************/
int _DrawPlanar_CreateStructures(DrawPlanarContext *context)
{
graphP theGraph = context->theGraph;
int VIsize = gp_PrimaryVertexIndexBound(theGraph);
int Esize = gp_EdgeIndexBound(theGraph);
if (theGraph->N <= 0)
return NOTOK;
if ((context->E = (DrawPlanar_EdgeRecP) malloc(Esize*sizeof(DrawPlanar_EdgeRec))) == NULL ||
(context->VI = (DrawPlanar_VertexInfoP) malloc(VIsize*sizeof(DrawPlanar_VertexInfo))) == NULL
)
{
return NOTOK;
}
return OK;
}
/********************************************************************
_DrawPlanar_InitStructures()
Intended to be called when N>0.
Initializes vertex and edge levels only. Graph level is
already initialized in _CreateStructures()
********************************************************************/
int _DrawPlanar_InitStructures(DrawPlanarContext *context)
{
#if NIL == 0
memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(DrawPlanar_VertexInfo));
memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(DrawPlanar_EdgeRec));
#else
int v, e, Esize;
graphP theGraph = context->theGraph;
if (theGraph->N <= 0)
return NOTOK;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
_DrawPlanar_InitVertexInfo(context, v);
Esize = gp_EdgeIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < Esize; e++)
_DrawPlanar_InitEdgeRec(context, e);
#endif
return OK;
}
/********************************************************************
_DrawPlanar_DupContext()
********************************************************************/
void *_DrawPlanar_DupContext(void *pContext, void *theGraph)
{
DrawPlanarContext *context = (DrawPlanarContext *) pContext;
DrawPlanarContext *newContext = (DrawPlanarContext *) malloc(sizeof(DrawPlanarContext));
if (newContext != NULL)
{
int VIsize = gp_PrimaryVertexIndexBound((graphP) theGraph);
int Esize = gp_EdgeIndexBound((graphP) theGraph);
*newContext = *context;
newContext->theGraph = (graphP) theGraph;
newContext->initialized = 0;
_DrawPlanar_ClearStructures(newContext);
if (((graphP) theGraph)->N > 0)
{
if (_DrawPlanar_CreateStructures(newContext) != OK)
{
_DrawPlanar_FreeContext(newContext);
return NULL;
}
// Initialize custom data structures by copying
memcpy(newContext->E, context->E, Esize*sizeof(DrawPlanar_EdgeRec));
memcpy(newContext->VI, context->VI, VIsize*sizeof(DrawPlanar_VertexInfo));
}
}
return newContext;
}
/********************************************************************
_DrawPlanar_FreeContext()
********************************************************************/
void _DrawPlanar_FreeContext(void *pContext)
{
DrawPlanarContext *context = (DrawPlanarContext *) pContext;
_DrawPlanar_ClearStructures(context);
free(pContext);
}
/********************************************************************
********************************************************************/
int _DrawPlanar_InitGraph(graphP theGraph, int N)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context == NULL)
return NOTOK;
theGraph->N = N;
theGraph->NV = N;
if (theGraph->arcCapacity == 0)
theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N;
if (_DrawPlanar_CreateStructures(context) != OK ||
_DrawPlanar_InitStructures(context) != OK)
return NOTOK;
context->functions.fpInitGraph(theGraph, N);
return OK;
}
/********************************************************************
********************************************************************/
void _DrawPlanar_ReinitializeGraph(graphP theGraph)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
// Reinitialize the graph
context->functions.fpReinitializeGraph(theGraph);
// Do the reinitialization that is specific to this module
_DrawPlanar_InitStructures(context);
}
}
/********************************************************************
The current implementation does not support an increase of arc
(edge record) capacity once the extension is attached to the graph
data structure. This is only due to not being necessary to support.
For now, it is easy to ensure the correct capacity before attaching
the extension, but support could be added later if there is some
reason to do so.
********************************************************************/
int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)
{
return NOTOK;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_SortVertices(graphP theGraph)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
// If this is a planarity-based algorithm to which graph drawing has been attached,
// and if the embedding process has already been completed
if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)
{
int v, vIndex;
DrawPlanar_VertexInfo temp;
// Relabel the context data members that indicate vertices
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
if (gp_IsVertex(context->VI[v].ancestor))
{
context->VI[v].ancestor = gp_GetVertexIndex(theGraph, context->VI[v].ancestor);
context->VI[v].ancestorChild = gp_GetVertexIndex(theGraph, context->VI[v].ancestorChild);
}
}
// "Sort" the extra vertex info associated with each vertex so that it is rearranged according
// to the index values of the vertices. This could be done very easily with an extra array in
// which, for each v, newVI[index of v] = VI[v]. However, this loop avoids memory allocation
// by performing the operation (almost) in-place, except for the pre-existing visitation flags.
_ClearVertexVisitedFlags(theGraph, FALSE);
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
// If the correct data has already been placed into position v
// by prior steps, then skip to the next vertex
if (gp_GetVertexVisited(theGraph, v))
continue;
// At the beginning of processing position v, the data in position v
// corresponds to data that belongs at the index of v.
vIndex = gp_GetVertexIndex(theGraph, v);
// Iterate on position v until it receives the correct data
while (!gp_GetVertexVisited(theGraph, v))
{
// Place the data at position v into its proper location at position
// vIndex, and move vIndex's data into position v.
temp = context->VI[v];
context->VI[v] = context->VI[vIndex];
context->VI[vIndex] = temp;
// The data at position vIndex is now marked as being correct.
gp_SetVertexVisited(theGraph, vIndex);
// The data now in position v is the data from position vIndex,
// whose index we now take as the new vIndex
vIndex = gp_GetVertexIndex(theGraph, vIndex);
}
}
}
if (context->functions.fpSortVertices(theGraph) != OK)
return NOTOK;
return OK;
}
return NOTOK;
}
/********************************************************************
Returns OK for a successful merge, NOTOK on an internal failure,
or NONEMBEDDABLE if the merge is blocked
********************************************************************/
int _DrawPlanar_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)
{
_CollectDrawingData(context, RootVertex, W, WPrevLink);
}
return context->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink);
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
int RetVal = context->functions.fpHandleInactiveVertex(theGraph, BicompRoot, pW, pWPrevLink);
if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)
{
if (_BreakTie(context, BicompRoot, *pW, *pWPrevLink) != OK)
return NOTOK;
}
return RetVal;
}
return NOTOK;
}
/********************************************************************
********************************************************************/
void _DrawPlanar_InitEdgeRec(DrawPlanarContext *context, int e)
{
context->E[e].pos = 0;
context->E[e].start = 0;
context->E[e].end = 0;
}
/********************************************************************
********************************************************************/
void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v)
{
context->VI[v].pos = 0;
context->VI[v].start = 0;
context->VI[v].end = 0;
context->VI[v].drawingFlag = DRAWINGFLAG_BEYOND;
context->VI[v].ancestorChild = NIL;
context->VI[v].ancestor = NIL;
context->VI[v].tie[0] = NIL;
context->VI[v].tie[1] = NIL;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
int RetVal = context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult);
if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)
{
if (RetVal == OK)
{
RetVal = _ComputeVisibilityRepresentation(context);
}
}
return RetVal;
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
if (context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph) != OK)
return NOTOK;
return _CheckVisibilityRepresentationIntegrity(context);
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
{
return OK;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
if (context->functions.fpReadPostprocess(theGraph, extraData, extraDataSize) != OK)
return NOTOK;
else if (extraData != NULL && extraDataSize > 0)
{
int v, e, tempInt, EsizeOccupied;
char line[64], tempChar;
sprintf(line, "<%s>", DRAWPLANAR_NAME);
// Find the start of the data for this feature
extraData = strstr(extraData, line);
if (extraData == NULL)
return NOTOK;
// Advance past the start tag
extraData = (void *) ((char *) extraData + strlen(line)+1);
// Read the N lines of vertex information
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar,
&context->VI[v].pos,
&context->VI[v].start,
&context->VI[v].end);
extraData = strchr(extraData, '\n') + 1;
}
// Read the lines that contain edge information
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++)
{
sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar,
&context->E[e].pos,
&context->E[e].start,
&context->E[e].end);
extraData = strchr(extraData, '\n') + 1;
}
}
return OK;
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _DrawPlanar_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize)
{
DrawPlanarContext *context = NULL;
gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
if (context != NULL)
{
if (context->functions.fpWritePostprocess(theGraph, pExtraData, pExtraDataSize) != OK)
return NOTOK;
else
{
int v, e, EsizeOccupied;
char line[64];
int maxLineSize = 64, extraDataPos = 0;
char *extraData = (char *) malloc((1 + theGraph->N + 2*theGraph->M + 1) * maxLineSize * sizeof(char));
int zeroBasedVertexOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0;
int zeroBasedEdgeOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstEdge(theGraph) : 0;
if (extraData == NULL)
return NOTOK;
// Bit of an unlikely case, but for safety, a bigger maxLineSize
// and line array size are needed to handle very large graphs
if (theGraph->N > 2000000000)
{
free(extraData);
return NOTOK;
}
sprintf(line, "<%s>\n", DRAWPLANAR_NAME);
strcpy(extraData+extraDataPos, line);
extraDataPos += (int) strlen(line);
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
sprintf(line, "%d: %d %d %d\n", v-zeroBasedVertexOffset,
context->VI[v].pos,
context->VI[v].start,
context->VI[v].end);
strcpy(extraData+extraDataPos, line);
extraDataPos += (int) strlen(line);
}
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++)
{
if (gp_EdgeInUse(theGraph, e))
{
sprintf(line, "%d: %d %d %d\n", e-zeroBasedEdgeOffset,
context->E[e].pos,
context->E[e].start,
context->E[e].end);
strcpy(extraData+extraDataPos, line);
extraDataPos += (int) strlen(line);
}
}
sprintf(line, "%s>\n", DRAWPLANAR_NAME);
strcpy(extraData+extraDataPos, line);
extraDataPos += (int) strlen(line);
*pExtraData = (void *) extraData;
*pExtraDataSize = extraDataPos * sizeof(char);
}
return OK;
}
return NOTOK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphEmbed.c 0000664 0000000 0000000 00000167013 12716155161 0023650 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "graph.h"
/* Imported functions */
extern void _ClearVertexVisitedFlags(graphP theGraph, int);
extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R);
extern int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R);
extern void _InitVertexRec(graphP theGraph, int v);
/* Private functions (some are exported to system only) */
int _EmbeddingInitialize(graphP theGraph);
void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
void _InvertVertex(graphP theGraph, int V);
void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R);
int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink);
void _WalkUp(graphP theGraph, int v, int e);
int _WalkDown(graphP theGraph, int v, int RootVertex);
int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R);
void _AdvanceFwdArcList(graphP theGraph, int v, int child, int nextChild);
int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
int _OrientVerticesInEmbedding(graphP theGraph);
int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
int _JoinBicomps(graphP theGraph);
/********************************************************************
gp_Embed()
Either a planar embedding is created in theGraph, or a Kuratowski
subgraph is isolated. Either way, theGraph remains sorted by DFI
since that is the most common desired result. The original vertex
numbers are available in the 'index' members of the vertex records.
Moreover, gp_SortVertices() can be invoked to put the vertices in
the order of the input graph, at which point the 'index' members of
the vertex records will contain the vertex DFIs.
return OK if the embedding was successfully created or no subgraph
homeomorphic to a topological obstruction was found.
NOTOK on internal failure
NONEMBEDDABLE if the embedding couldn't be created due to
the existence of a subgraph homeomorphic to a
topological obstruction.
For core planarity, OK is returned when theGraph contains a planar
embedding of the input graph, and NONEMBEDDABLE is returned when a
subgraph homeomorphic to K5 or K3,3 has been isolated in theGraph.
Extension modules can overload functions used by gp_Embed to achieve
alternate algorithms. In those cases, the return results are
similar. For example, a K3,3 search algorithm would return
NONEMBEDDABLE if it finds the K3,3 obstruction, and OK if the graph
is planar or only contains K5 homeomorphs. Similarly, an
outerplanarity module can return OK for an outerplanar embedding or
NONEMBEDDABLE when a subgraph homeomorphic to K2,3 or K4 has been
isolated.
The algorithm extension for gp_Embed() is encoded in the embedFlags,
and the details of the return value can be found in the extension
module that defines the embedding flag.
********************************************************************/
int gp_Embed(graphP theGraph, int embedFlags)
{
int v, e, c;
int RetVal = OK;
// Basic parameter checks
if (theGraph==NULL)
return NOTOK;
// Preprocessing
theGraph->embedFlags = embedFlags;
// Allow extension algorithms to postprocess the DFS
if (theGraph->functions.fpEmbeddingInitialize(theGraph) != OK)
return NOTOK;
// In reverse DFI order, embed the back edges from each vertex to its DFS descendants.
for (v = gp_GetLastVertex(theGraph); gp_VertexInRangeDescending(theGraph, v); v--)
{
RetVal = OK;
// Walkup calls establish Pertinence in Step v
// Do the Walkup for each cycle edge from v to a DFS descendant W.
e = gp_GetVertexFwdArcList(theGraph, v);
while (gp_IsArc(e))
{
theGraph->functions.fpWalkUp(theGraph, v, e);
e = gp_GetNextArc(theGraph, e);
if (e == gp_GetVertexFwdArcList(theGraph, v))
e = NIL;
}
gp_SetVertexPertinentRootsList(theGraph, v, NIL);
// Work systematically through the DFS children of vertex v, using Walkdown
// to add the back edges from v to its descendants in each of the DFS subtrees
c = gp_GetVertexSortedDFSChildList(theGraph, v);
while (gp_IsVertex(c))
{
if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, c)))
{
RetVal = theGraph->functions.fpWalkDown(theGraph, v, gp_GetRootFromDFSChild(theGraph, c));
// If Walkdown returns OK, then it is OK to proceed with edge addition.
// Otherwise, if Walkdown returns NONEMBEDDABLE then we stop edge addition.
if (RetVal != OK)
break;
}
c = gp_GetVertexNextDFSChild(theGraph, v, c);
}
// If the Walkdown determined that the graph is NONEMBEDDABLE,
// then the guiding embedder loop can be stopped now.
if (RetVal != OK)
break;
}
// Postprocessing to orient the embedding and merge any remaining separated bicomps.
// Some extension algorithms may overload this function, e.g. to do nothing if they
// have no need of an embedding.
return theGraph->functions.fpEmbedPostprocess(theGraph, v, RetVal);
}
/********************************************************************
_EmbeddingInitialize()
This method performs the following tasks:
(1) Assign depth first index (DFI) and DFS parentvalues to vertices
(2) Assign DFS edge types
(3) Create a sortedDFSChildList for each vertex, sorted by child DFI
(4) Create a sortedFwdArcList for each vertex, sorted by descendant DFI
(5) Assign leastAncestor values to vertices
(6) Sort the vertices by their DFIs
(7) Initialize for pertinence and future pertinence management
(8) Embed each tree edge as a singleton biconnected component
The first five of these are performed in a single-pass DFS of theGraph.
Afterward, the vertices are sorted by their DFIs, the lowpoint values
are assigned and then the DFS tree edges stored in virtual vertices
during the DFS are used to create the DFS tree embedding.
********************************************************************/
int _EmbeddingInitialize(graphP theGraph)
{
stackP theStack;
int DFI, v, R, uparent, u, uneighbor, e, f, eTwin, ePrev, eNext;
int leastValue, child;
#ifdef PROFILE
platform_time start, end;
platform_GetTime(start);
#endif
gp_LogLine("graphEmbed.c/_EmbeddingInitialize() start\n");
theStack = theGraph->theStack;
// At most we push 2 integers per edge from a vertex to each *unvisited* neighbor
// plus one additional integer to help detect post-processing. This is less
// than the 2 * arcCapacity integer stack that is already in theGraph structure,
// so we make sure it's still there and cleared, then we clear all vertex
// visited flags in prep for the Depth first search operation. */
if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph))
return NOTOK;
sp_ClearStack(theStack);
_ClearVertexVisitedFlags(theGraph, FALSE);
// This outer loop processes each connected component of a disconnected graph
// No need to compare v < N since DFI will reach N when inner loop processes the
// last connected component in the graph
for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++)
{
// Skip numbered vertices to cause the outerloop to find the
// next DFS tree root in a disconnected graph
if (gp_IsVertex(gp_GetVertexParent(theGraph, v)))
continue;
// DFS a connected component
sp_Push2(theStack, NIL, NIL);
while (sp_NonEmpty(theStack))
{
sp_Pop2(theStack, uparent, e);
// For vertex uparent and edge e, obtain the opposing endpoint u of e
// If uparent is NIL, then e is also NIL and we have encountered the
// false edge to the DFS tree root as pushed above.
u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e);
// We popped an edge to an unvisited vertex, so it is either a DFS tree edge
// or a false edge to the DFS tree root (u).
if (!gp_GetVertexVisited(theGraph, u))
{
gp_LogLine(gp_MakeLogStr3("v=%d, DFI=%d, parent=%d", u, DFI, uparent));
// (1) Set the DFI and DFS parent
gp_SetVertexVisited(theGraph, u);
gp_SetVertexIndex(theGraph, u, DFI++);
gp_SetVertexParent(theGraph, u, uparent);
if (gp_IsArc(e))
{
// (2) Set the edge type values for tree edges
gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD);
gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT);
// (3) Record u in the sortedDFSChildList of uparent
gp_SetVertexSortedDFSChildList(theGraph, uparent,
gp_AppendDFSChild(theGraph, uparent, gp_GetVertexIndex(theGraph, u)));
// (8) Record e as the first and last arc of the virtual vertex R,
// a root copy of uparent uniquely associated with child u
R = gp_GetRootFromDFSChild(theGraph, gp_GetVertexIndex(theGraph, u));
gp_SetFirstArc(theGraph, R, e);
gp_SetLastArc(theGraph, R, e);
}
// (5) Initialize the least ancestor value
gp_SetVertexLeastAncestor(theGraph, u, gp_GetVertexIndex(theGraph, u));
// Push edges to all unvisited neighbors. These will be either
// tree edges to children or forward arcs of back edges
// Edges not pushed are marked as back edges here, except the
// edge leading back to the immediate DFS parent.
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)))
{
sp_Push2(theStack, u, e);
}
else if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_PARENT)
{
// (2) Set the edge type values for back edges
gp_SetEdgeType(theGraph, e, EDGE_TYPE_BACK);
eTwin = gp_GetTwinArc(theGraph, e);
gp_SetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD);
// (4) Move the twin of back edge record e to the sortedFwdArcList of the ancestor
uneighbor = gp_GetNeighbor(theGraph, e);
ePrev = gp_GetPrevArc(theGraph, eTwin);
eNext = gp_GetNextArc(theGraph, eTwin);
if (gp_IsArc(ePrev))
gp_SetNextArc(theGraph, ePrev, eNext);
else gp_SetFirstArc(theGraph, uneighbor, eNext);
if (gp_IsArc(eNext))
gp_SetPrevArc(theGraph, eNext, ePrev);
else gp_SetLastArc(theGraph, uneighbor, ePrev);
if (gp_IsArc(f = gp_GetVertexFwdArcList(theGraph, uneighbor)))
{
ePrev = gp_GetPrevArc(theGraph, f);
gp_SetPrevArc(theGraph, eTwin, ePrev);
gp_SetNextArc(theGraph, eTwin, f);
gp_SetPrevArc(theGraph, f, eTwin);
gp_SetNextArc(theGraph, ePrev, eTwin);
}
else
{
gp_SetVertexFwdArcList(theGraph, uneighbor, eTwin);
gp_SetPrevArc(theGraph, eTwin, eTwin);
gp_SetNextArc(theGraph, eTwin, eTwin);
}
// (5) Update the leastAncestor value for the vertex u
uneighbor = gp_GetVertexIndex(theGraph, uneighbor);
if (uneighbor < gp_GetVertexLeastAncestor(theGraph, u))
gp_SetVertexLeastAncestor(theGraph, u, uneighbor);
}
e = gp_GetNextArc(theGraph, e);
}
}
}
}
// The graph is now DFS numbered
theGraph->internalFlags |= FLAGS_DFSNUMBERED;
// (6) Now that all vertices have a DFI in the index member, we can sort vertices
if (gp_SortVertices(theGraph) != OK)
return NOTOK;
// Loop through the vertices and virtual vertices to...
for (v = gp_GetLastVertex(theGraph); gp_VertexInRangeDescending(theGraph, v); v--)
{
// (7) Initialize for pertinence management
gp_SetVertexVisitedInfo(theGraph, v, theGraph->N);
// (7) Initialize for future pertinence management
child = gp_GetVertexSortedDFSChildList(theGraph, v);
gp_SetVertexFuturePertinentChild(theGraph, v, child);
leastValue = gp_GetVertexLeastAncestor(theGraph, v);
while (gp_IsVertex(child))
{
if (leastValue > gp_GetVertexLowpoint(theGraph, child))
leastValue = gp_GetVertexLowpoint(theGraph, child);
child = gp_GetVertexNextDFSChild(theGraph, v, child);
}
gp_SetVertexLowpoint(theGraph, v, leastValue);
// (8) Create the DFS tree embedding using the child edge records stored in the virtual vertices
// For each vertex v that is a DFS child, the virtual vertex R that will represent v's parent
// in the singleton bicomp with v is at location v + N in the vertex array.
if (gp_IsDFSTreeRoot(theGraph, v))
{
gp_SetFirstArc(theGraph, v, NIL);
gp_SetLastArc(theGraph, v, NIL);
}
else
{
R = gp_GetRootFromDFSChild(theGraph, v);
// Make the child edge the only edge in the virtual vertex adjacency list
e = gp_GetFirstArc(theGraph, R);
gp_SetPrevArc(theGraph, e, NIL);
gp_SetNextArc(theGraph, e, NIL);
// Reset the twin's neighbor value to point to the virtual vertex
eTwin = gp_GetTwinArc(theGraph, e);
gp_SetNeighbor(theGraph, eTwin, R);
// Make its twin the only edge in the child's adjacency list
gp_SetFirstArc(theGraph, v, eTwin);
gp_SetLastArc(theGraph, v, eTwin);
gp_SetPrevArc(theGraph, eTwin, NIL);
gp_SetNextArc(theGraph, eTwin, NIL);
// Set up the external face management data structure to match
gp_SetExtFaceVertex(theGraph, R, 0, v);
gp_SetExtFaceVertex(theGraph, R, 1, v);
gp_SetExtFaceVertex(theGraph, v, 0, R);
gp_SetExtFaceVertex(theGraph, v, 1, R);
}
}
gp_LogLine("graphEmbed.c/_EmbeddingInitialize() end\n");
#ifdef PROFILE
platform_GetTime(end);
printf("Initialize embedding in %.3lf seconds.\n", platform_GetDuration(start,end));
#endif
return OK;
}
/********************************************************************
_EmbedBackEdgeToDescendant()
The Walkdown has found a descendant vertex W to which it can
attach a back edge up to the root of the bicomp it is processing.
The RootSide and WPrevLink indicate the parts of the external face
that will be replaced at each endpoint of the back edge.
********************************************************************/
void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink)
{
int fwdArc, backArc, parentCopy;
/* We get the two edge records of the back edge to embed.
The Walkup recorded in W's adjacentTo the index of the forward arc
from the root's parent copy to the descendant W. */
fwdArc = gp_GetVertexPertinentEdge(theGraph, W);
backArc = gp_GetTwinArc(theGraph, fwdArc);
/* The forward arc is removed from the fwdArcList of the root's parent copy. */
parentCopy = gp_GetPrimaryVertexFromRoot(theGraph, RootVertex);
gp_LogLine(gp_MakeLogStr5("graphEmbed.c/_EmbedBackEdgeToDescendant() V=%d, R=%d, R_out=%d, W=%d, W_in=%d",
parentCopy, RootVertex, RootSide, W, WPrevLink));
if (gp_GetVertexFwdArcList(theGraph, parentCopy) == fwdArc)
{
gp_SetVertexFwdArcList(theGraph, parentCopy, gp_GetNextArc(theGraph, fwdArc));
if (gp_GetVertexFwdArcList(theGraph, parentCopy) == fwdArc)
gp_SetVertexFwdArcList(theGraph, parentCopy, NIL);
}
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc));
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc));
// The forward arc is added to the adjacency list of the RootVertex.
// Note that we're guaranteed that the RootVertex adjacency list is non-empty,
// so tests for NIL are not needed
gp_SetAdjacentArc(theGraph, fwdArc, 1^RootSide, NIL);
gp_SetAdjacentArc(theGraph, fwdArc, RootSide, gp_GetArc(theGraph, RootVertex, RootSide));
gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, RootVertex, RootSide), 1^RootSide, fwdArc);
gp_SetArc(theGraph, RootVertex, RootSide, fwdArc);
// The back arc is added to the adjacency list of W.
// The adjacency list of W is also guaranteed non-empty
gp_SetAdjacentArc(theGraph, backArc, 1^WPrevLink, NIL);
gp_SetAdjacentArc(theGraph, backArc, WPrevLink, gp_GetArc(theGraph, W, WPrevLink));
gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, W, WPrevLink), 1^WPrevLink, backArc);
gp_SetArc(theGraph, W, WPrevLink, backArc);
gp_SetNeighbor(theGraph, backArc, RootVertex);
/* Link the two endpoint vertices together on the external face */
gp_SetExtFaceVertex(theGraph, RootVertex, RootSide, W);
gp_SetExtFaceVertex(theGraph, W, WPrevLink, RootVertex);
}
/********************************************************************
_InvertVertex()
This function flips the orientation of a single vertex such that
instead of using link successors to go clockwise (or counterclockwise)
around a vertex's adjacency list, link predecessors would be used.
********************************************************************/
void _InvertVertex(graphP theGraph, int W)
{
int e, temp;
gp_LogLine(gp_MakeLogStr1("graphEmbed.c/_InvertVertex() W=%d", W));
// Swap the links in all the arcs of the adjacency list
e = gp_GetFirstArc(theGraph, W);
while (gp_IsArc(e))
{
temp = gp_GetNextArc(theGraph, e);
gp_SetNextArc(theGraph, e, gp_GetPrevArc(theGraph, e));
gp_SetPrevArc(theGraph, e, temp);
e = temp;
}
// Swap the first/last edge record indicators in the vertex
temp = gp_GetFirstArc(theGraph, W);
gp_SetFirstArc(theGraph, W, gp_GetLastArc(theGraph, W));
gp_SetLastArc(theGraph, W, temp);
// Swap the first/last external face indicators in the vertex
temp = gp_GetExtFaceVertex(theGraph, W, 0);
gp_SetExtFaceVertex(theGraph, W, 0, gp_GetExtFaceVertex(theGraph, W, 1));
gp_SetExtFaceVertex(theGraph, W, 1, temp);
}
/********************************************************************
_MergeVertex()
The merge step joins the vertex W to the root R of a child bicompRoot,
which is a root copy of W appearing in the region N to 2N-1.
Actually, the first step of this is to redirect all of the edges leading
into R so that they indicate W as the neighbor instead of R.
For each edge node pointing to R, we set the 'v' field to W. Once an
edge is redirected from a root copy R to a parent copy W, the edge is
never redirected again, so we associate the cost of the redirection
as constant per edge, which maintains linear time performance.
After this is done, a regular circular list union occurs. The only
consideration is that WPrevLink is used to indicate the two edge
records e_w and e_r that will become consecutive in the resulting
adjacency list of W. We set e_w to W's link [WPrevLink] and e_r to
R's link [1^WPrevLink] so that e_w and e_r indicate W and R with
opposing links, which become free to be cross-linked. Finally,
the edge record e_ext, set equal to R's link [WPrevLink], is the edge
that, with e_r, held R to the external face. Now, e_ext will be the
new link [WPrevLink] edge record for W. If e_w and e_r become part
of a proper face, then e_ext and W's link [1^WPrevLink] are the two
edges that attach W to the external face cycle of the containing bicomp.
********************************************************************/
void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R)
{
int e, eTwin, e_w, e_r, e_ext;
gp_LogLine(gp_MakeLogStr4("graphEmbed.c/_MergeVertex() W=%d, W_in=%d, R=%d, R_out=%d",
W, WPrevLink, R, 1^WPrevLink));
// All arcs leading into R from its neighbors must be changed
// to say that they are leading into W
e = gp_GetFirstArc(theGraph, R);
while (gp_IsArc(e))
{
eTwin = gp_GetTwinArc(theGraph, e);
gp_GetNeighbor(theGraph, eTwin) = W;
e = gp_GetNextArc(theGraph, e);
}
// Obtain the edge records involved in the list union
e_w = gp_GetArc(theGraph, W, WPrevLink);
e_r = gp_GetArc(theGraph, R, 1^WPrevLink);
e_ext = gp_GetArc(theGraph, R, WPrevLink);
// If W has any edges, then join the list with that of R
if (gp_IsArc(e_w))
{
// The WPrevLink arc of W is e_w, so the 1^WPrevLink arc in e_w leads back to W.
// Now it must lead to e_r. Likewise, e_r needs to lead back to e_w with the
// opposing link, which is WPrevLink
// Note that the adjacency lists of W and R are guaranteed non-empty, which is
// why these linkages can be made without NIL tests.
gp_SetAdjacentArc(theGraph, e_w, 1^WPrevLink, e_r);
gp_SetAdjacentArc(theGraph, e_r, WPrevLink, e_w);
// Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext
gp_SetArc(theGraph, W, WPrevLink, e_ext);
gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, NIL);
}
// Otherwise, W just receives R's list. This happens, for example, on a
// DFS tree root vertex during JoinBicomps()
else
{
// Cross-link W's 1^WPrevLink arc and the WPrevLink arc in e_r
gp_SetArc(theGraph, W, 1^WPrevLink, e_r);
gp_SetAdjacentArc(theGraph, e_r, WPrevLink, NIL);
// Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext
gp_SetArc(theGraph, W, WPrevLink, e_ext);
gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, NIL);
}
// Erase the entries in R, which is a root copy that is no longer needed
_InitVertexRec(theGraph, R);
}
/********************************************************************
_MergeBicomps()
Merges all biconnected components at the cut vertices indicated by
entries on the stack.
theGraph contains the stack of bicomp roots and cut vertices to merge
v, RootVertex, W and WPrevLink are not used in this routine, but are
used by overload extensions
Returns OK, but an extension function may return a value other than
OK in order to cause Walkdown to terminate immediately.
********************************************************************/
int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink)
{
int R, Rout, Z, ZPrevLink, e, extFaceVertex;
while (sp_NonEmpty(theGraph->theStack))
{
sp_Pop2(theGraph->theStack, R, Rout);
sp_Pop2(theGraph->theStack, Z, ZPrevLink);
/* The external faces of the bicomps containing R and Z will
form two corners at Z. One corner will become part of the
internal face formed by adding the new back edge. The other
corner will be the new external face corner at Z.
We first want to update the links at Z to reflect this. */
extFaceVertex = gp_GetExtFaceVertex(theGraph, R, 1^Rout);
gp_SetExtFaceVertex(theGraph, Z, ZPrevLink, extFaceVertex);
if (gp_GetExtFaceVertex(theGraph, extFaceVertex, 0) == gp_GetExtFaceVertex(theGraph, extFaceVertex, 1))
// When (R, extFaceVertex) form a singleton bicomp, they have the same orientation, so the Rout link in extFaceVertex
// is the one that has to now point back to Z
gp_SetExtFaceVertex(theGraph, extFaceVertex, Rout, Z);
else
// When R and extFaceVertex are not alone in the bicomp, then they may not have the same orientation, so the
// ext face link that should point to Z is whichever one pointed to R, since R is a root copy of Z.
gp_SetExtFaceVertex(theGraph, extFaceVertex, gp_GetExtFaceVertex(theGraph, extFaceVertex, 0) == R ? 0 : 1, Z);
/* If the path used to enter Z is opposed to the path
used to exit R, then we have to flip the bicomp
rooted at R, which we signify by inverting R
then setting the sign on its DFS child edge to
indicate that its descendants must be flipped later */
if (ZPrevLink == Rout)
{
Rout = 1^ZPrevLink;
if (gp_GetFirstArc(theGraph, R) != gp_GetLastArc(theGraph, R))
_InvertVertex(theGraph, R);
e = gp_GetFirstArc(theGraph, R);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
{
// The core planarity algorithm could simply "set" the inverted flag
// because a bicomp root edge cannot be already inverted in the core
// planarity algorithm at the time of this merge.
// However, extensions may perform edge reductions on tree edges, resulting
// in an inversion sign being promoted to the root edge of a bicomp before
// it gets merged. So, xor is used to reverse the inversion flag on the
// root edge if the bicomp root must be inverted before it is merged.
gp_XorEdgeFlagInverted(theGraph, e);
break;
}
e = gp_GetNextArc(theGraph, e);
}
}
// R is no longer pertinent to Z since we are about to merge R into Z, so we delete R
// from Z's pertinent bicomp list (Walkdown gets R from the head of the list).
gp_DeleteVertexPertinentRoot(theGraph, Z, R);
// If the merge will place the current future pertinence child into the same bicomp as Z,
// then we advance to the next child (or NIL) because future pertinence is
if (gp_GetDFSChildFromRoot(theGraph, R) == gp_GetVertexFuturePertinentChild(theGraph, Z))
{
gp_SetVertexFuturePertinentChild(theGraph, Z,
gp_GetVertexNextDFSChild(theGraph, Z, gp_GetVertexFuturePertinentChild(theGraph, Z)));
}
// Now we push R into Z, eliminating R
theGraph->functions.fpMergeVertex(theGraph, Z, ZPrevLink, R);
}
return OK;
}
/********************************************************************
_WalkUp()
v is the vertex currently being embedded
e is the forward arc of the back edge to a descendant W of v
The Walkup establishes pertinence for step v. It marks W with e
as a way of indicating it is pertinent because it should be made
'adjacent to' v by adding a back edge (v', W), which will occur when
the Walkdown encounters W.
The Walkup also determines the pertinent child bicomps that should be
set up as a result of the need to embed edge (v, W). It does this by
recording the pertinent child biconnected components of all cut
vertices between W and the child of v that is an ancestor of W.
Note that it stops the traversal if it finds a visited info value set
to v, which indicates that a prior walkup call in step v has already
done the work. This ensures work is not duplicated.
A second technique used to maintain a total linear time bound for the
whole planarity method is that of parallel external face traversal.
This ensures that the cost of determining pertinence in step v is
linearly commensurate with the length of the path that ultimately
is removed from the external face.
Zig and Zag are so named because one goes around one side of a bicomp
and the other goes around the other side, yet we have as yet no notion
of orientation for the bicomp. The edge record e from vertex v gestures
to a descendant vertex W in some other bicomp. Zig and Zag start out
at W. They go around alternate sides of the bicomp until its root is
found. We then hop from the root copy to the parent copy of the vertex
in order to record which bicomp we just came from and also to continue
the walk-up at the parent copy as if it were the new W. We reiterate
this process until the parent copy actually is v, at which point the
Walkup is done.
********************************************************************/
void _WalkUp(graphP theGraph, int v, int e)
{
int W = gp_GetNeighbor(theGraph, e);
int Zig=W, Zag=W, ZigPrevLink=1, ZagPrevLink=0;
int nextZig, nextZag, R;
// Start by marking W as being directly pertinent
gp_SetVertexPertinentEdge(theGraph, W, e);
// Zig and Zag are initialized at W, and we continue looping around
// the external faces of bicomps up from W until we reach vertex v
// (or until the visited info optimization breaks the loop)
while (Zig != v)
{
// Obtain the next vertex in a first direction and determine if it is a bicomp root
if (gp_IsVirtualVertex(theGraph, (nextZig = gp_GetExtFaceVertex(theGraph, Zig, 1^ZigPrevLink))))
{
// If the current vertex along the external face was visited in this step v,
// then the bicomp root and its ancestor roots have already been added.
if (gp_GetVertexVisitedInfo(theGraph, Zig) == v) break;
// Store the bicomp root that was found
R = nextZig;
// Since the bicomp root was the next vertex on the path from Zig, determine the
// vertex on the opposing path that enters the bicomp root.
nextZag = gp_GetExtFaceVertex(theGraph, R,
gp_GetExtFaceVertex(theGraph, R, 0)==Zig ? 1 : 0);
// If the opposing vertex was already marked visited in this step, then a prior
// Walkup already recorded as pertinent the bicomp root and its ancestor roots.
if (gp_GetVertexVisitedInfo(theGraph, nextZag) == v) break;
}
// Obtain the next vertex in the parallel direction and perform the analogous logic
else if (gp_IsVirtualVertex(theGraph, (nextZag = gp_GetExtFaceVertex(theGraph, Zag, 1^ZagPrevLink))))
{
if (gp_GetVertexVisitedInfo(theGraph, Zag) == v) break;
R = nextZag;
nextZig = gp_GetExtFaceVertex(theGraph, R,
gp_GetExtFaceVertex(theGraph, R, 0)==Zag ? 1 : 0);
if (gp_GetVertexVisitedInfo(theGraph, nextZig) == v) break;
}
// The bicomp root was not found in either direction.
else
{
if (gp_GetVertexVisitedInfo(theGraph, Zig) == v) break;
if (gp_GetVertexVisitedInfo(theGraph, Zag) == v) break;
R = NIL;
}
// This Walkup has now finished with another vertex along each of the parallel
// paths, so they are marked visited in step v so that future Walkups in this
// step v can break if these vertices are encountered again.
gp_SetVertexVisitedInfo(theGraph, Zig, v);
gp_SetVertexVisitedInfo(theGraph, Zag, v);
// If both directions found new non-root vertices, then proceed with parallel external face traversal
if (gp_IsNotVertex(R))
{
ZigPrevLink = gp_GetExtFaceVertex(theGraph, nextZig, 0)==Zig ? 0 : 1;
Zig = nextZig;
ZagPrevLink = gp_GetExtFaceVertex(theGraph, nextZag, 0)==Zag ? 0 : 1;
Zag = nextZag;
}
// The bicomp root was found and not previously recorded as pertinent,
// so walk up to the parent bicomp and continue
else
{
// Step up from the root (virtual) vertex to the primary (non-virtual) vertex
Zig = Zag = gp_GetPrimaryVertexFromRoot(theGraph, R);
ZigPrevLink = 1;
ZagPrevLink = 0;
// Add the new root vertex to the list of pertinent bicomp roots of the primary vertex.
// The new root vertex is appended if future pertinent and prepended if only pertinent
// so that, by virtue of storage, the Walkdown will process all pertinent bicomps that
// are not future pertinent before any future pertinent bicomps.
// NOTE: Unlike vertices, the activity status of a bicomp is computed solely using
// the lowpoint of the DFS child in the bicomp's root edge, which indicates
// whether the DFS child or any of its descendants connect by a back edge to
// ancestors of v. If so, then the bicomp rooted at RootVertex must contain a
// future pertinent vertex that must be kept on the external face.
if (gp_GetVertexLowpoint(theGraph, gp_GetDFSChildFromRoot(theGraph, R)) < v)
gp_AppendVertexPertinentRoot(theGraph, Zig, R);
else gp_PrependVertexPertinentRoot(theGraph, Zag, R);
}
}
}
/********************************************************************
_WalkDown()
Consider a circular shape with small circles and squares along its perimeter.
The small circle at the top is the root vertex of the bicomp. The other small
circles represent active vertices, and the squares represent future pertinent
vertices. The root vertex is a root copy of v, the vertex currently being processed.
The Walkup previously marked all vertices adjacent to v by setting their
pertinentEdge members with the forward arcs of the back edges to embed.
Two Walkdown traversals are performed to visit all reachable vertices
along each of the external face paths emanating from RootVertex (a root
copy of vertex v) to embed back edges to descendants of vertex v that
have their pertinentEdge members marked.
During each Walkdown traversal, it is sometimes necessary to hop from a
vertex to one of its child biconnected components in order to reach the
desired vertices. In such cases, the biconnected components are merged
such that adding the back edge forms a new proper face in the biconnected
component rooted at RootVertex (which, again, is a root copy of v).
The outer loop performs both walks, unless the first walk got all the way
around to RootVertex (only happens when bicomp contains no external activity,
such as when processing the last vertex), or when non-planarity is
discovered (in a pertinent child bicomp such that the stack is non-empty).
For the inner loop, each iteration visits a vertex W. If W is marked as
requiring a back edge, then MergeBicomps is called to merge the biconnected
components whose cut vertices have been collecting in merge stack. Then,
the back edge (RootVertex, W) is added, and the pertinentEdge of W is cleared.
Next, we check whether W has a pertinent child bicomp. If so, then we figure
out which path down from the root of the child bicomp leads to the next vertex
to be visited, and we push onto the stack information on the cut vertex and
the paths used to enter into it and exit from it. Alternately, if W
had no pertinent child bicomps, then we check to see if it is inactive.
If so, we find the next vertex along the external face, then short-circuit
its inactive predecessor (under certain conditions). Finally, if W is not
inactive, but it has no pertinent child bicomps, then we already know its
adjacentTo flag is clear so both criteria for internal activity also fail.
Therefore, W must be a stopping vertex.
A stopping vertex X is a future pertinent vertex that has no pertinent
child bicomps and no unembedded back edge to the current vertex v.
The inner loop of Walkdown stops walking when it reaches a stopping vertex X
because if it were to proceed beyond X and embed a back edge, then X would be
surrounded by the bounding cycle of the bicomp. This would clearly be
incorrect because X has a path leading from it to an ancestor of v, which
would have to cross the bounding cycle.
Either Walkdown traversal can halt the Walkdown and return if a pertinent
child biconnected component to which the traversal has descended is blocked,
i.e. has stopping vertices on both paths emanating from the root. This
indicates an obstruction to embedding. In core planarity it is evidence of
a K_{3,3}, but some extension algorithms are able to clear the blockage and
proceed with embedding.
If both Walkdown traversals successfully completed, then the outer loop
ends. Post-processing code tests whether the Walkdown embedded all the
back edges from v to its descendants in the subtree rooted by c, a DFS
child of v uniquely associated with the RootVertex. If not, then embedding
was obstructed. In core planarity it is evidence of a K_{3,3} or K_5, but some
extension algorithms are able to clear the blockage and proceed with embedding.
Returns OK if all possible edges were embedded,
NONEMBEDDABLE if less than all possible edges were embedded,
NOTOK for an internal code failure
********************************************************************/
int _WalkDown(graphP theGraph, int v, int RootVertex)
{
int RetVal, W, WPrevLink, R, X, XPrevLink, Y, YPrevLink, RootSide, e;
int RootEdgeChild = gp_GetDFSChildFromRoot(theGraph, RootVertex);
sp_ClearStack(theGraph->theStack);
for (RootSide = 0; RootSide < 2; RootSide++)
{
W = gp_GetExtFaceVertex(theGraph, RootVertex, RootSide);
// Determine the link used to enter W based on which side points back to RootVertex
// Implicitly handled special case: In core planarity, the first Walkdown traversal
// Will be on a singleton edge. In this case, RootVertex and W are *consistently*
// oriented, and the RootSide is 0, so WPrevLink should be 1. This calculation is
// written to implicitly produce that result.
WPrevLink = gp_GetExtFaceVertex(theGraph, W, 1) == RootVertex ? 1 : 0;
while (W != RootVertex)
{
// Detect unembedded back edge descendant endpoint W
if (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, W)))
{
// Merge any bicomps whose cut vertices were traversed to reach W, then add the
// edge to W to form a new proper face in the embedding.
if (sp_NonEmpty(theGraph->theStack))
{
if ((RetVal = theGraph->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink)) != OK)
return RetVal;
}
theGraph->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink);
// Clear W's pertinentEdge since the forward arc it contained has been embedded
gp_SetVertexPertinentEdge(theGraph, W, NIL);
}
// If W has a pertinent child bicomp, then we descend to the first one...
if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W)))
{
// Push the vertex W and the direction of entry, then descend to a root copy R of W
sp_Push2(theGraph->theStack, W, WPrevLink);
R = gp_GetVertexFirstPertinentRoot(theGraph, W);
// Get the next active vertices X and Y on the external face paths emanating from R
X = gp_GetExtFaceVertex(theGraph, R, 0);
XPrevLink = gp_GetExtFaceVertex(theGraph, X, 1)==R ? 1 : 0;
Y = gp_GetExtFaceVertex(theGraph, R, 1);
YPrevLink = gp_GetExtFaceVertex(theGraph, Y, 0)==R ? 0 : 1;
// Now we implement the Walkdown's simple path selection rules!
// Select a direction from the root to a pertinent vertex,
// preferentially toward a vertex that is not future pertinent
gp_UpdateVertexFuturePertinentChild(theGraph, X, v);
gp_UpdateVertexFuturePertinentChild(theGraph, Y, v);
if (PERTINENT(theGraph, X) && NOTFUTUREPERTINENT(theGraph, X, v))
{
W = X;
WPrevLink = XPrevLink;
sp_Push2(theGraph->theStack, R, 0);
}
else if (PERTINENT(theGraph, Y) && NOTFUTUREPERTINENT(theGraph, Y, v))
{
W = Y;
WPrevLink = YPrevLink;
sp_Push2(theGraph->theStack, R, 1);
}
else if (PERTINENT(theGraph, X))
{
W = X;
WPrevLink = XPrevLink;
sp_Push2(theGraph->theStack, R, 0);
}
else if (PERTINENT(theGraph, Y))
{
W = Y;
WPrevLink = YPrevLink;
sp_Push2(theGraph->theStack, R, 1);
}
else
{
// Both the X and Y sides of the descendant bicomp are blocked.
// Let the application decide whether it can unblock the bicomp.
// The core planarity/outerplanarity embedder simply isolates a
// planarity/outerplanary obstruction and returns NONEMBEDDABLE
if ((RetVal = theGraph->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R)) != OK)
return RetVal;
// If an extension algorithm cleared the blockage, then we pop W and WPrevLink
// back off the stack and let the Walkdown traversal try descending again
sp_Pop2(theGraph->theStack, W, WPrevLink);
}
}
else
{
// The vertex W is known to be non-pertinent, so if it is future pertinent
// (or if the algorithm is based on outerplanarity), then the vertex is
// a stopping vertex for the Walkdown traversal.
gp_UpdateVertexFuturePertinentChild(theGraph, W, v);
if (FUTUREPERTINENT(theGraph, W, v) || (theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR))
{
// Create an external face short-circuit between RootVertex and the stopping vertex W
// so that future steps do not walk down a long path of inactive vertices between them.
// As a special case, we ensure that the external face is not reduced to just two
// vertices, W and RootVertex, because it would then become a challenge to determine
// whether W has the same orientation as RootVertex.
// So, if the other side of RootVertex is already attached to W, then we simply push
// W back one vertex so that the external face will have at least three vertices.
if (gp_GetExtFaceVertex(theGraph, RootVertex, 1^RootSide) == W)
{
X = W;
W = gp_GetExtFaceVertex(theGraph, W, WPrevLink);
WPrevLink = gp_GetExtFaceVertex(theGraph, W, 0) == X ? 1 : 0;
}
gp_SetExtFaceVertex(theGraph, RootVertex, RootSide, W);
gp_SetExtFaceVertex(theGraph, W, WPrevLink, RootVertex);
// Terminate the Walkdown traversal since it encountered the stopping vertex
break;
}
// If the vertex is neither pertinent nor future pertinent, then it is inactive.
// The default handler planarity handler simply skips inactive vertices by traversing
// to the next vertex on the external face.
// Once upon a time, false edges called short-circuit edges were added to eliminate
// inactive vertices, but the extFace links above achieve the same result with less work.
else
{
if (theGraph->functions.fpHandleInactiveVertex(theGraph, RootVertex, &W, &WPrevLink) != OK)
return NOTOK;
}
}
}
}
// Detect and handle the case in which Walkdown was blocked from embedding all the back edges from v
// to descendants in the subtree of the child of v associated with the bicomp RootVertex.
if (gp_IsArc(e = gp_GetVertexFwdArcList(theGraph, v)) && RootEdgeChild < gp_GetNeighbor(theGraph, e))
{
int nextChild = gp_GetVertexNextDFSChild(theGraph, v, RootEdgeChild);
// The Walkdown was blocked from embedding all forward arcs into the RootEdgeChild subtree
// if there the next child's DFI is greater than the descendant endpoint of the next forward arc,
// or if there is no next child.
if (gp_IsNotVertex(nextChild) || nextChild > gp_GetNeighbor(theGraph, e))
{
// If an extension indicates it is OK to proceed despite the unembedded forward arcs, then
// advance to the forward arcs for the next child, if any
if ((RetVal = theGraph->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, RootVertex)) == OK)
_AdvanceFwdArcList(theGraph, v, RootEdgeChild, nextChild);
return RetVal;
}
}
return OK;
}
/********************************************************************
_HandleBlockedBicomp()
A biconnected component has blocked the Walkdown from embedding
back edges. Each external face path emanating from the root is
blocked by a stopping vertex.
The core planarity/outerplanarity algorithm handles the blockage
by isolating an embedding obstruction (a subgraph homeomorphic to
K_{3,3} or K_5 for planarity, or a subgraph homeomorphic to K_{2,3}
or K_4 for outerplanarity). Then NONEMBEDDABLE is returned so that
the WalkDown can terminate.
Extension algorithms are able to clear some of the blockages, in
which case OK is returned to indicate that the WalkDown can proceed.
Returns OK to proceed with WalkDown at W,
NONEMBEDDABLE to terminate WalkDown of Root Vertex
NOTOK for internal error
********************************************************************/
int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R)
{
int RetVal = NONEMBEDDABLE;
if (R != RootVertex)
sp_Push2(theGraph->theStack, R, 0);
if (theGraph->embedFlags == EMBEDFLAGS_PLANAR)
{
if (_IsolateKuratowskiSubgraph(theGraph, v, RootVertex) != OK)
RetVal = NOTOK;
}
else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)
{
if (_IsolateOuterplanarObstruction(theGraph, v, RootVertex) != OK)
RetVal = NOTOK;
}
return RetVal;
}
/********************************************************************
_AdvanceFwdArcList()
If an extension determines that it is OK to leave some forward arcs
unembedded, then we advance the forward arc list head pointer past
the unembedded arcs for the current child so that it points to the
first forward arc for the next child, if any.
There are two meanings of the phrase "if any". First, there may be
no next child, in which case nextChild is NIL, and the forward arc
list need not be advanced.
If there is a next child, then the forward arc list head needs to
be advanced to the first arc whose descendant endpoint is greater
than the nextChild, if any. However, the tail end of the forward arc
list may include unembedded forward arcs to a preceding sibling
of the child vertex. So, we advance an edge pointer e until one of
the following happens:
1) e gets all the way around to the forward arc list head
2) e finds an edge whose descendant endpoint is less than the child
3) e finds an edge whose descendant endpoint is greater than the next child
In case 1, all the forward arcs belong in the subtree of the child, so
there is no need to change the forward arc list head.
In case 2, there are no more forward arcs to any following siblings of
the child, only left-behind unembedded forward arcs that we advanced
past in previous calls to this method from Walkdowns of the preceding
children of v. So the forward arc list head should be set to e so that
it is set to the forward arc with the least numbered descendant endpoint.
In case 3, the desired forward arc into the subtree of a following sibling
of the child has been found, so again the forward arc list head should be
set to e to indicate that edge.
After all Walkdowns of the children of a vertex, the forward arc list will
be NIL if all edges were embedded, or it will indicate the unembedded
forward arc whose descendant endpoint has the least number. Cases 1 and 2
directly implement this in cases where a Walkdown for the given child
fails to embed an edge, and case 3 indirectly finishes the job by making
sure the forward arc list head has the right value at the beginning of
a Walkdown for a particular child. If the Walkdown of that child succeeds
at embedding all the forward edges into that child's subtree, then each
embedding advances the forward arc list head. So, even if the Walkdown
of the last pertinent child embeds all forward arcs, then the Walkdown
itself advances the forward arc list head to the first unembedded forward
arc, or to NIL.
********************************************************************/
void _AdvanceFwdArcList(graphP theGraph, int v, int child, int nextChild)
{
int e = gp_GetVertexFwdArcList(theGraph, v);
while (gp_IsArc(e))
{
// 2) e finds an edge whose descendant endpoint is less than the child
if (gp_GetNeighbor(theGraph, e) < child)
{
gp_SetVertexFwdArcList(theGraph, v, e);
break;
}
// 3) e finds an edge whose descendant endpoint is greater than the next child
else if (gp_IsVertex(nextChild) && nextChild < gp_GetNeighbor(theGraph, e))
{
gp_SetVertexFwdArcList(theGraph, v, e);
break;
}
e = gp_GetNextArc(theGraph, e);
// 1) e gets all the way around to the forward arc list head
if (e == gp_GetVertexFwdArcList(theGraph, v))
e = NIL;
}
}
/********************************************************************
_HandleInactiveVertex()
Although it is possible to short-circuit every inactive vertex from
the external face, for efficiency the Walkdown traversal now just does
a single short-circuit between the bicomp root and a stopping vertex.
This is because the main thing that short-circuiting needs to optimize
is the Walkdown's choice of direction after descending to the root
of a pertinent biconnected component. So, the Walkdown just optimizes
the external face of a biconnected component as it finishes processing
it so it will be ready in future steps when it becomes pertinent.
Hence, when traversing the face of a bicomp during the current step,
we only need to skip an inactive vertex and traverse to the next vertex
on the external face.
********************************************************************/
int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink)
{
int X = gp_GetExtFaceVertex(theGraph, *pW, 1^*pWPrevLink);
*pWPrevLink = gp_GetExtFaceVertex(theGraph, X, 0) == *pW ? 0 : 1;
*pW = X;
return OK;
}
/********************************************************************
_EmbedPostprocess()
After the loop that embeds the cycle edges from each vertex to its
DFS descendants, this method is invoked to postprocess the graph.
If the graph is planar or outerplanar, then a consistent orientation
is imposed on the vertices of the embedding, and any remaining
separated biconnected components are joined together.
If the graph is non-planar or non-outerplanar, then an obstruction
to planarity or outerplanarity has already been isolated.
Extensions may override this function to provide alternate behavior.
@param theGraph - the graph ready for postprocessing
@param v - the last vertex processed by the edge embedding loop
@param edgeEmbeddingResult -
OK if all edge embedding iterations returned OK
NONEMBEDDABLE if an embedding iteration failed to embed
all edges for a vertex
@return NOTOK on internal failure
NONEMBEDDABLE if a subgraph homeomorphic to a topological
obstruction is isolated in the graph
OK otherwise (e.g. if the graph contains an embedding)
*****************************************************************/
int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
{
int RetVal = edgeEmbeddingResult;
// If an embedding was found, then post-process the embedding structure give
// a consistent orientation to all vertices then eliminate virtual vertices
if (edgeEmbeddingResult == OK)
{
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_JoinBicomps(theGraph) != OK)
RetVal = NOTOK;
}
// If the graph is embedded (OK) or NONEMBEDDABLE, we pass the result back
return RetVal;
}
/********************************************************************
_OrientVerticesInEmbedding()
Each vertex will then have an orientation, either clockwise or
counterclockwise. All vertices in each bicomp need to have the
same orientation.
This method clears the stack, and the stack is clear when it
is finished.
Returns OK on success, NOTOK on implementation failure.
********************************************************************/
int _OrientVerticesInEmbedding(graphP theGraph)
{
int R;
sp_ClearStack(theGraph->theStack);
// For each vertex, obtain the associated bicomp root location and,
// if it is still in use as a bicomp root, orient the vertices in the bicomp
for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++)
{
if (gp_VirtualVertexInUse(theGraph, R))
{
if (_OrientVerticesInBicomp(theGraph, R, 0) != OK)
return NOTOK;
}
}
return OK;
}
/********************************************************************
_OrientVerticesInBicomp()
As a result of the work done so far, the edges around each vertex have
been put in order, but the orientation may be counterclockwise or
clockwise for different vertices within the same bicomp.
We need to reverse the orientations of those vertices that are not
oriented the same way as the root of the bicomp.
During embedding, a bicomp with root edge (v', c) may need to be flipped.
We do this by inverting the root copy v' and implicitly inverting the
orientation of the vertices in the subtree rooted by c by assigning -1
to the sign of the DFSCHILD edge record leading to c.
We now use these signs to help propagate a consistent vertex orientation
throughout all vertices that have been merged into the given bicomp.
The bicomp root contains the orientation to be imposed on all parent
copy vertices. We perform a standard depth first search to visit each
vertex. A vertex must be inverted if the product of the edge signs
along the tree edges between the bicomp root and the vertex is -1.
Finally, the PreserveSigns flag, if set, performs the inversions
but does not change any of the edge signs. This allows a second
invocation of this function to restore the state of the bicomp
as it was before the first call.
This method uses the stack but preserves whatever may have been
on it. In debug mode, it will return NOTOK if the stack overflows.
This method pushes at most two integers per vertext in the bicomp.
Returns OK on success, NOTOK on implementation failure.
********************************************************************/
int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns)
{
int W, e, invertedFlag;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push2(theGraph->theStack, BicompRoot, 0);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
/* Pop a vertex to orient */
sp_Pop2(theGraph->theStack, W, invertedFlag);
/* Invert the vertex if the inverted flag is set */
if (invertedFlag)
_InvertVertex(theGraph, W);
/* Push the vertex's DFS children that are in the bicomp */
e = gp_GetFirstArc(theGraph, W);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
{
sp_Push2(theGraph->theStack, gp_GetNeighbor(theGraph, e),
invertedFlag ^ gp_GetEdgeFlagInverted(theGraph, e));
if (!PreserveSigns)
gp_ClearEdgeFlagInverted(theGraph, e);
}
e = gp_GetNextArc(theGraph, e);
}
}
return OK;
}
/********************************************************************
_JoinBicomps()
The embedding algorithm works by only joining bicomps once the result
forms a larger bicomp. However, if the original graph was separable
or disconnected, then the result of the embed function will be a
graph that contains each bicomp as a distinct entity. This function
merges the bicomps into one connected graph.
********************************************************************/
int _JoinBicomps(graphP theGraph)
{
int R;
for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++)
{
// If the root is still active (i.e. an in-use virtual vertex)
// then merge it with its primary (non-virtual) counterpart
if (gp_VirtualVertexInUse(theGraph, R))
_MergeVertex(theGraph, gp_GetPrimaryVertexFromRoot(theGraph, R), 0, R);
}
return OK;
}
/****************************************************************************
_OrientExternalFacePath()
The vertices along the path (v ... w) are assumed to be degree two vertices
in an external face path connecting u and x. This method imparts the
orientation of u and x onto the vertices v ... w.
The work done is on the order of the path length.
Returns OK if the external face path was oriented, NOTOK on implementation
error (i.e. if a condition arises providing the path is not on the
external face).
****************************************************************************/
int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x)
{
int e_u, e_v, e_ulink, e_vlink;
// Get the edge record in u that indicates v; uses the twinarc method to
// ensure the cost is dominated by the degree of v (which is 2), not u
// (which can be any degree).
e_u = gp_GetTwinArc(theGraph, gp_GetNeighborEdgeRecord(theGraph, v, u));
do {
// Get the external face link in vertex u that indicates the
// edge e_u which connects to the next vertex v in the path
// As a sanity check, we determine whether e_u is an
// external face edge, because there would be an internal
// implementation error if not
if (gp_GetFirstArc(theGraph, u) == e_u)
e_ulink = 0;
else if (gp_GetLastArc(theGraph, u) == e_u)
e_ulink = 1;
else return NOTOK;
v = gp_GetNeighbor(theGraph, e_u);
// Now get the external face link in vertex v that indicates the
// edge e_v which connects back to the prior vertex u.
e_v = gp_GetTwinArc(theGraph, e_u);
if (gp_GetFirstArc(theGraph, v) == e_v)
e_vlink = 0;
else if (gp_GetLastArc(theGraph, v) == e_v)
e_vlink = 1;
else return NOTOK;
// The vertices u and v are inversely oriented if they
// use the same link to indicate the edge [e_u, e_v].
if (e_vlink == e_ulink)
{
_InvertVertex(theGraph, v);
e_vlink = 1^e_vlink;
}
// This update of the extFace short-circuit is polite but unnecessary.
// This orientation only occurs once we know we can isolate a K_{3,3},
// at which point the extFace data structure is not used.
gp_SetExtFaceVertex(theGraph, u, e_ulink, v);
gp_SetExtFaceVertex(theGraph, v, e_vlink, u);
u = v;
e_u = gp_GetArc(theGraph, v, 1^e_vlink);
} while (u != x);
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphExtensions.c 0000664 0000000 0000000 00000052026 12716155161 0024770 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include
#include "appconst.h"
#include "graphExtensions.private.h"
#include "graphExtensions.h"
#include "graphFunctionTable.h"
/* Imported functions */
extern void _InitFunctionTable(graphP theGraph);
/* Private function */
void _FreeExtension(graphExtensionP extension);
void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions);
void _FixupFunctionTables(graphP theGraph, graphExtensionP curr);
graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex);
/********************************************************************
* The moduleIDGenerator is used to help ensure that all extensions
* added during a run-time have a different integer identifier.
* An ID identifies an extension, which may be added to multiple
* graphs. It is used in lieu of identifying extensions by a string
* name, which is noticeably expensive when a frequently called
* overload function seeks the extension context for a graph.
********************************************************************/
static int moduleIDGenerator = 0;
/********************************************************************
The extension mechanism allows new modules to equip a graph with the
data structures and functions needed to implement new algorithms
without impeding the performance of the core graph planar embedding
algorithms on graphs that have not been so equipped.
The following steps must be used to create a graph extension:
1) Create a moduleID variable initialized to zero that will be
assigned a positive integer the first time the extension is
added to a graph by gp_AddExtension()
2) Define an extension context structure to contain all of the data
and function pointers that extend the graph. The context must
include a graphFunctionTable to allow overloading of functions.
An instance of this context structure is passed to the "context"
parameter of gp_AddExtension().
3) Define a function capable of duplicating your context data
structure. It receives a void pointer indicating the context
to duplicate and a void pointer that can be cast to a graph
pointer indicating the graph for which the context is being
duplicated. The void pointer returned indicates the newly
allocated context structure. The pointer to this function is
passed to the "dupContext" parameter of gp_AddExtension()
Note: It is useful to store in your context structure a pointer
to the graph that the context is extending. There are certain
function overloads you will perform that will only receive
the context, and you may need to know things about the graph,
such as the number of vertices or edges.
4) Define a function that can free the memory used by your context
data structure. It will receive a void pointer indicating the
instance of your context data structure that you passed as the
"context" parameter to gp_AddExtension().
The free function pointer should be passed as the "freeContext"
parameter to gp_AddExtension()
5) The expected method of attaching your feature to a graph is to
create a function called gp_AttachFeature(), where 'Feature' is
the name of your module. The attach function allocates your context
data structure, initializes the extension data, assigns overload
function pointers, and invokes gp_AddExtension().
NOTE: It is advisable to use memset on the context function table
before assigning any function overloads because any function not
being overloaded must have a NULL pointer.
NOTE: The gp_AddExtension() method puts the overload function
pointers into the graph's function table, and the base function
pointers that were overloaded are placed in the context function
table. This allows the extension's functions to have access to
base function behaviors, since many extension functions will
extend rather than replace the base behavior.
6) There are a few functions that you must overload in order to
successfully manage data structures that are parallel to the
main graph data structures.
The core graph data structure has function pointers to functions
that can be overloaded. In addition to invoking gp_AddExtension(),
you need to set pointers to your own versions of the functions
you are overloading. You will also need to store a copy of the
prior pointer in your feature's context data structure so that you
can invoke the "base" behavior from your function overload, e.g.
if your feature is attached but not active or if your feature
augments the base behavior rather than replacing it.
a) If any kind of data structures needs to be maintained at
the graph, vertex or edge levels, then an overload of
fpInitGraph() will be needed.
b) If any data must be associated with primary and virtual vertices,
then an overload of fpInitVertexRec() is needed. If data must be
associated only with primary vertices (0 to N-1), then one can
overload fpInitVertexInfo() instead.
The overload function should be named _Feature_InitVertexRec()
or _Feature_InitVertexInfo().
It will invoke the base fpInitVertexRec() or fpInitVertexInfo()
but then also invoke a second function named _InitFeatureVertexRec()
or _InitFeatureVertexInfo() thatinitializes the custom VertexRec
or VertexInfo data members.
c) If any data must be associated with the edges, then an overload
of fpInitEdgeRec() is needed.
This overload function should be named _Feature_InitEdgeRec().
It will invoke the base fpInitEdgeRec() and also invoke
a second function named_InitFeatureEdgeRec() that
initializes the custom EdgeRec data members
d) If any graph-level data structures are needed, then an
overload of fpReinitializeGraph() will also be needed, not just the
overload of fpInitGraph(). However, if only vertex-level and/or
edge level data members are needed, then the overloads of
fpInitVertexRec(), fpInitVertexInfo() and/or fpInitEdgeRec() are
invoked by the basic fpReinitializeGraph without needing to overload
it as well.
e) If any data must be persisted in the file format, then overloads
of fpReadPostprocess() and fpWritePostprocess() are needed.
7) Define internal functions for _Feature_ClearStructures(),
_Feature_CreateStructures() and _Feature_InitStructures();
a) The _Feature_ClearStructures() should simply null out pointers
to extra structures on its first invocation, but thereafter it
should free them and then null them. Since the null-only step
is done only once in gp_AttachFeature(), it seems reasonable to
not bother with a more complicated _Feature_ClearStructures().
But, as an extension is developed, the data structures change,
so it is best to keep all this logic in one place.
b) The _Feature_CreateStructures() should just allocate memory for
but not initialize any vertex level and edge level data structures.
Data structures maintained at the graph level, such as a stack or a
list collection, should be created _and_ initialized.
c) The _Feature_InitStructures() should invoke just the functions
needed to initialize the custom VertexRec, VertexInfo and EdgeRec
data members, if any.
8) Define a function gp_DetachFeature() that invokes gp_RemoveExtension()
This should be done for consistency, so that users of a feature
do not attach it with gp_AttachFeature() and remove it with
gp_RemoveExtension(). However, it may sometimes be necessary to
run more code than just gp_RemoveExtension() when detaching a feature,
e.g. some final result values of a feature may be saved to data
available in the core graph or in other features.
********************************************************************/
/********************************************************************
gp_AddExtension()
@param theGraph - pointer to the graph to which the extension is being added
@param pModuleID - address of the variable that contains the feature's
extension identifier. If the variable is equal to zero,
it is assigned a positive number. Thereafter, the variable
value can be used to find and remove the extension from any graph
@param context - the data storage for the extension being added
The context is owned by the extension and freed with freeContext()
@param dupContext - a function capable of duplicating the context data
@param freeContext - a function capable of freeing the context data
@param functions - pointer to a table of functions stored in the data context.
The table of functions is an input and output parameter.
On input, the table consists of new function pointers
for functions being overloaded.
Any function not being overloaded must be NULL.
The non-NULL function pointers are used to overload
the functions in the graph, and the prior pointer values
in the graph are stored in the function table as output.
The context data therefore has the pointer to the base
function corresponding to any function its extension
module overloaded.
The new extension is created and added to the graph.
********************************************************************/
int gp_AddExtension(graphP theGraph,
int *pModuleID,
void *context,
void *(*dupContext)(void *, void *),
void (*freeContext)(void *),
graphFunctionTableP functions)
{
graphExtensionP newExtension = NULL;
if (theGraph == NULL || pModuleID == NULL ||
context == NULL || dupContext == NULL || freeContext == NULL ||
functions == NULL)
{
return NOTOK;
}
// If the extension already exists, then don't redefine it.
if (gp_FindExtension(theGraph, *pModuleID, NULL) == TRUE)
{
return NOTOK;
}
// Assign a unique ID to the extension if it does not already have one
if (*pModuleID == 0)
{
*pModuleID = ++moduleIDGenerator;
}
// Allocate the new extension
if ((newExtension = (graphExtensionP) malloc(sizeof(graphExtension))) == NULL)
{
return NOTOK;
}
// Assign the data payload of the extension
newExtension->moduleID = *pModuleID;
newExtension->context = context;
newExtension->dupContext = dupContext;
newExtension->freeContext = freeContext;
newExtension->functions = functions;
_OverloadFunctions(theGraph, functions);
// Make the new linkages
newExtension->next = (struct graphExtension *) theGraph->extensions;
theGraph->extensions = newExtension;
// The new extension was successfully added
return OK;
}
/********************************************************************
_OverloadFunctions()
For each non-NULL function pointer, the pointer becomes the new value
for the function in the graph, and the old function pointer in the graph
is placed in the overload table.
This way, when an extension function is invoked, it can choose to invoke
the base function before or after whatever extension behavior it provides.
Also, when it comes time to remove an extension, this extension system
has access to the overload tables of all extensions so that it can unhook
the functions of the module being removed from the chains of calls for
each overloaded function. This will involve some pointer changes in
the overload tables of extensions other than the one being removed.
********************************************************************/
void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions)
{
void **graphFunctionTable = (void **) &theGraph->functions;
void **newFunctionTable = (void **) functions;
int numFunctions = sizeof(theGraph->functions) / sizeof(void *);
int K;
for (K = 0; K < numFunctions; K++)
{
if (newFunctionTable[K] != NULL)
{
void *fp = graphFunctionTable[K];
graphFunctionTable[K] = newFunctionTable[K];
newFunctionTable[K] = fp;
}
}
}
/********************************************************************
gp_FindExtension()
@param theGraph - the graph whose extension list is to be searched
@param moduleID - the identifier of the module whose extension context is desired
@param pContext - the return parameter that receives the value of the
extension, if found. This may be NULL if the extension was
not found or if the extension context value was NULL.
@return TRUE if the extension was found, NOTOK if not found
If FALSE is returned, then the context returned is guaranteed to be NULL
If TRUE is returned, the context returned may be NULL if that is the
current value of the module extension
********************************************************************/
int gp_FindExtension(graphP theGraph, int moduleID, void **pContext)
{
graphExtensionP first = NULL, next = NULL;
if (pContext != NULL)
{
*pContext = NULL;
}
if (theGraph==NULL || moduleID==0)
{
return FALSE;
}
first = theGraph->extensions;
while (first != NULL)
{
next = (graphExtensionP) first->next;
if (first->moduleID == moduleID)
{
if (pContext != NULL)
{
*pContext = first->context;
}
return TRUE;
}
first = next;
}
return FALSE;
}
/********************************************************************
gp_GetExtension()
Calling this function is equivalent to invoking gp_FindExtension()
except that some debuggers have difficulty stepping into a function
that (properly) start by setting a local variable pointer to NULL
when the debugger has watch expressions that dereference a pointer
of the same name. In such cases,
MyContext *context = NULL;
gp_FindExtension(theGraph, MYEXTENSION_ID, &context);
can be replaced by
MyContext *context = gp_GetExtension(theGraph, MYEXTENSION_ID);
@param theGraph - the graph whose extension list is to be searched
@param moduleID - the identifier of the module whose extension context is desired
@return void pointer to the extension if found, or NULL if not found.
********************************************************************/
void *gp_GetExtension(graphP theGraph, int moduleID)
{
void *context = NULL;
int result = gp_FindExtension(theGraph, moduleID, &context);
return result ? context : NULL;
}
/********************************************************************
gp_RemoveExtension()
@param theGraph - the graph from which to remove an extension
@param moduleID - the ID of the module whose extension context is to be removed
@return OK if the module is successfully removed or not in the list
NOTOK for internal errors, such as invalid parameters
********************************************************************/
int gp_RemoveExtension(graphP theGraph, int moduleID)
{
graphExtensionP prev, curr, next;
if (theGraph==NULL || moduleID==0)
return NOTOK;
prev = NULL;
curr = theGraph->extensions;
while (curr != NULL)
{
next = (graphExtensionP) curr->next;
if (curr->moduleID == moduleID)
break;
prev = curr;
curr = next;
}
// An extension can only be removed if it is found. Otherwise,
// we return OK because the extension degenerately removed
// (since it is already gone)
if (curr != NULL)
{
_FixupFunctionTables(theGraph, curr);
// Unhook the curr extension
if (prev != NULL)
prev->next = (struct graphExtension *) next;
else theGraph->extensions = next;
// Free the curr extension
_FreeExtension(curr);
}
return OK;
}
/********************************************************************
_FixupFunctionTables()
Removes the functions in the curr function table from the function
call lists established by the function tables of all extensions and
theGraph.
Since new extensions are prepended, extensions before curr may
have further overloaded the functions in the curr function table.
For a non-NULL function pointer in the curr table, if there is
a preceding extension with the same function pointer non-NULL, then
the function table of the closest such preceding extension points
to the original overload function of the curr extension, and the
curr extension contains the pointer to the base function behavior,
so now the function table of that preceding extension must be changed
to the function pointer value in the curr extension.
********************************************************************/
void _FixupFunctionTables(graphP theGraph, graphExtensionP curr)
{
void **currFunctionTable = (void **) (curr->functions);
int numFunctions = sizeof(*(curr->functions)) / sizeof(void *);
int K;
for (K = 0; K < numFunctions; K++)
{
if (currFunctionTable[K] != NULL)
{
void **nearestOverloadFunctionTable = (void **) &theGraph->functions;
graphExtensionP pred = _FindNearestOverload(theGraph, curr, K);
if (pred != NULL)
nearestOverloadFunctionTable = (void **) pred->functions;
nearestOverloadFunctionTable[K] = currFunctionTable[K];
}
}
}
/********************************************************************
_FindNearestOverload()
********************************************************************/
graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex)
{
graphExtensionP curr = theGraph->extensions;
graphExtensionP found = NULL;
void **functionTable;
while (curr != target)
{
functionTable = (void **) curr->functions;
if (functionTable[functionIndex] != NULL)
found = curr;
curr = (graphExtensionP) curr->next;
}
return found;
}
/********************************************************************
gp_CopyExtensions()
********************************************************************/
int gp_CopyExtensions(graphP dstGraph, graphP srcGraph)
{
graphExtensionP next = NULL, newNext = NULL, newLast = NULL;
if (srcGraph == NULL || dstGraph == NULL)
return NOTOK;
gp_FreeExtensions(dstGraph);
next = srcGraph->extensions;
while (next != NULL)
{
if ((newNext = (graphExtensionP) malloc(sizeof(graphExtension))) == NULL)
{
gp_FreeExtensions(dstGraph);
return NOTOK;
}
newNext->moduleID = next->moduleID;
newNext->context = next->dupContext(next->context, dstGraph);
newNext->dupContext = next->dupContext;
newNext->freeContext = next->freeContext;
newNext->functions = next->functions;
newNext->next = NULL;
if (newLast != NULL)
newLast->next = (struct graphExtension *) newNext;
else
dstGraph->extensions = newNext;
newLast = newNext;
next = (graphExtensionP) next->next;
}
return OK;
}
/********************************************************************
gp_FreeExtensions()
@param pFirst - pointer to head pointer of graph extension list
Each graph extension is freed, including invoking the freeContext
function provided when the extension was added.
********************************************************************/
void gp_FreeExtensions(graphP theGraph)
{
if (theGraph != NULL)
{
graphExtensionP curr = theGraph->extensions;
graphExtensionP next = NULL;
while (curr != NULL)
{
next = (graphExtensionP) curr->next;
_FreeExtension(curr);
curr = next;
}
theGraph->extensions = NULL;
_InitFunctionTable(theGraph);
}
}
/********************************************************************
_FreeExtension()
********************************************************************/
void _FreeExtension(graphExtensionP extension)
{
if (extension->context != NULL && extension->freeContext != NULL)
{
extension->freeContext(extension->context);
}
free(extension);
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphExtensions.h 0000664 0000000 0000000 00000001545 12716155161 0024775 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_EXTENSIONS_H
#define GRAPH_EXTENSIONS_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "graphStructures.h"
int gp_AddExtension(graphP theGraph,
int *pModuleID,
void *context,
void *(*dupContext)(void *, void *),
void (*freeContext)(void *),
graphFunctionTableP overloadTable);
int gp_FindExtension(graphP theGraph, int moduleID, void **pContext);
void *gp_GetExtension(graphP theGraph, int moduleID);
int gp_RemoveExtension(graphP theGraph, int moduleID);
int gp_CopyExtensions(graphP dstGraph, graphP srcGraph);
void gp_FreeExtensions(graphP theGraph);
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphExtensions.private.h 0000664 0000000 0000000 00000001122 12716155161 0026435 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_EXTENSIONS_PRIVATE_H
#define GRAPH_EXTENSIONS_PRIVATE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphFunctionTable.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int moduleID;
void *context;
void *(*dupContext)(void *, void *);
void (*freeContext)(void *);
graphFunctionTableP functions;
struct graphExtension *next;
} graphExtension;
typedef graphExtension * graphExtensionP;
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphFunctionTable.h 0000664 0000000 0000000 00000003363 12716155161 0025373 0 ustar 00root root 0000000 0000000 #ifndef GRAPHFUNCTIONTABLE_H
#define GRAPHFUNCTIONTABLE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
NOTE: If you add any FUNCTION POINTERS to this function table, then you must
also initialize them in _InitFunctionTable() in graphUtils.c.
*/
typedef struct
{
// These function pointers allow extension modules to overload some of
// the behaviors of protected functions. Only advanced applications
// will overload these functions
int (*fpEmbeddingInitialize)();
void (*fpEmbedBackEdgeToDescendant)();
void (*fpWalkUp)();
int (*fpWalkDown)();
int (*fpMergeBicomps)();
void (*fpMergeVertex)();
int (*fpHandleInactiveVertex)();
int (*fpHandleBlockedBicomp)();
int (*fpEmbedPostprocess)();
int (*fpMarkDFSPath)();
int (*fpCheckEmbeddingIntegrity)();
int (*fpCheckObstructionIntegrity)();
// These function pointers allow extension modules to overload some
// of the behaviors of gp_* function in the public API
int (*fpInitGraph)();
void (*fpReinitializeGraph)();
int (*fpEnsureArcCapacity)();
int (*fpSortVertices)();
int (*fpReadPostprocess)();
int (*fpWritePostprocess)();
void (*fpHideEdge)();
void (*fpRestoreEdge)();
int (*fpHideVertex)();
int (*fpRestoreVertex)();
int (*fpContractEdge)();
int (*fpIdentifyVertices)();
} graphFunctionTable;
typedef graphFunctionTable * graphFunctionTableP;
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphIO.c 0000664 0000000 0000000 00000063102 12716155161 0023135 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include
#include "graph.h"
/* Private functions (exported to system) */
int _ReadAdjMatrix(graphP theGraph, FILE *Infile);
int _ReadAdjList(graphP theGraph, FILE *Infile);
int _WriteAdjList(graphP theGraph, FILE *Outfile);
int _WriteAdjMatrix(graphP theGraph, FILE *Outfile);
int _WriteDebugInfo(graphP theGraph, FILE *Outfile);
/********************************************************************
_ReadAdjMatrix()
This function reads the undirected graph in upper triangular matrix format.
Though O(N^2) time is required, this routine is useful during
reliability testing due to the wealth of graph generating software
that uses this format for output.
Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges
********************************************************************/
int _ReadAdjMatrix(graphP theGraph, FILE *Infile)
{
int N, v, w, Flag;
if (Infile == NULL) return NOTOK;
fscanf(Infile, " %d ", &N);
if (gp_InitGraph(theGraph, N) != OK)
return NOTOK;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
gp_SetVertexIndex(theGraph, v, v);
for (w = v+1; gp_VertexInRange(theGraph, w); w++)
{
fscanf(Infile, " %1d", &Flag);
if (Flag)
{
if (gp_AddEdge(theGraph, v, 0, w, 0) != OK)
return NOTOK;
}
}
}
return OK;
}
/********************************************************************
_ReadAdjList()
This function reads the graph in adjacency list format.
The file format is
On the first line : N= number of vertices
On N subsequent lines: #: a b c ... -1
where # is a vertex number and a, b, c, ... are its neighbors.
NOTE: The vertex number is for file documentation only. It is an
error if the vertices are not in sorted order in the file.
NOTE: If a loop edge is found, it is ignored without error.
NOTE: This routine supports digraphs. For a directed arc (v -> W),
an edge record is created in both vertices, v and W, and the
edge record in v's adjacency list is marked OUTONLY while the
edge record in W's list is marked INONLY.
This makes it easy to used edge directedness when appropriate
but also seamlessly process the corresponding undirected graph.
Returns: OK on success, NONEMBEDDABLE if success except too many edges
NOTOK on file content error (or internal error)
********************************************************************/
int _ReadAdjList(graphP theGraph, FILE *Infile)
{
int N, v, W, adjList, e, indexValue, ErrorCode;
int zeroBased = FALSE;
if (Infile == NULL) return NOTOK;
fgetc(Infile); /* Skip the N= */
fgetc(Infile);
fscanf(Infile, " %d ", &N); /* Read N */
if (gp_InitGraph(theGraph, N) != OK)
{
printf("Failed to init graph");
return NOTOK;
}
// Clear the visited members of the vertices so they can be used
// during the adjacency list read operation
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
gp_SetVertexVisitedInfo(theGraph, v, NIL);
// Do the adjacency list read operation for each vertex in order
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
// Read the vertex number
fscanf(Infile, "%d", &indexValue);
if (indexValue == 0 && v == gp_GetFirstVertex(theGraph))
zeroBased = TRUE;
indexValue += zeroBased ? gp_GetFirstVertex(theGraph) : 0;
gp_SetVertexIndex(theGraph, v, indexValue);
// The vertices are expected to be in numeric ascending order
if (gp_GetVertexIndex(theGraph, v) != v)
return NOTOK;
// Skip the colon after the vertex number
fgetc(Infile);
// If the vertex already has a non-empty adjacency list, then it is
// the result of adding edges during processing of preceding vertices.
// The list is removed from the current vertex v and saved for use
// during the read operation for v. Adjacencies to preceding vertices
// are pulled from this list, if present, or added as directed edges
// if not. Adjacencies to succeeding vertices are added as undirected
// edges, and will be corrected later if the succeeding vertex does not
// have the matching adjacency using the following mechanism. After the
// read operation for a vertex v, any adjacency nodes left in the saved
// list are converted to directed edges from the preceding vertex to v.
adjList = gp_GetFirstArc(theGraph, v);
if (gp_IsArc(adjList))
{
// Store the adjacency node location in the visited member of each
// of the preceding vertices to which v is adjacent so that we can
// efficiently detect the adjacency during the read operation and
// efficiently find the adjacency node.
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), e);
e = gp_GetNextArc(theGraph, e);
}
// Make the adjacency list circular, for later ease of processing
gp_SetPrevArc(theGraph, adjList, gp_GetLastArc(theGraph, v));
gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), adjList);
// Remove the list from the vertex
gp_SetFirstArc(theGraph, v, NIL);
gp_SetLastArc(theGraph, v, NIL);
}
// Read the adjacency list.
while (1)
{
// Read the value indicating the next adjacent vertex (or the list end)
fscanf(Infile, " %d ", &W);
W += zeroBased ? gp_GetFirstVertex(theGraph) : 0;
// A value below the valid range indicates the adjacency list end
if (W < gp_GetFirstVertex(theGraph))
break;
// A value above the valid range is an error
if (W > gp_GetLastVertex(theGraph))
return NOTOK;
// Loop edges are not supported
else if (W == v)
return NOTOK;
// If the adjacency is to a succeeding, higher numbered vertex,
// then we'll add an undirected edge for now
else if (v < W)
{
if ((ErrorCode = gp_AddEdge(theGraph, v, 0, W, 0)) != OK)
return ErrorCode;
}
// If the adjacency is to a preceding, lower numbered vertex, then
// we have to pull the adjacency node from the preexisting adjList,
// if it is there, and if not then we have to add a directed edge.
else
{
// If the adjacency node (arc) already exists, then we add it
// as the new first arc of the vertex and delete it from adjList
if (gp_IsArc(gp_GetVertexVisitedInfo(theGraph, W)))
{
e = gp_GetVertexVisitedInfo(theGraph, W);
// Remove the arc e from the adjList construct
gp_SetVertexVisitedInfo(theGraph, W, NIL);
if (adjList == e)
{
if ((adjList = gp_GetNextArc(theGraph, e)) == e)
adjList = NIL;
}
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e));
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e));
gp_AttachFirstArc(theGraph, v, e);
}
// If an adjacency node to the lower numbered vertex W does not
// already exist, then we make a new directed arc from the current
// vertex v to W.
else
{
// It is added as the new first arc in both vertices
if ((ErrorCode = gp_AddEdge(theGraph, v, 0, W, 0)) != OK)
return ErrorCode;
// Note that this call also sets OUTONLY on the twin arc
gp_SetDirection(theGraph, gp_GetFirstArc(theGraph, W), EDGEFLAG_DIRECTION_INONLY);
}
}
}
// If there are still adjList entries after the read operation
// then those entries are not representative of full undirected edges.
// Rather, they represent incoming directed arcs from other vertices
// into vertex v. They need to be added back into v's adjacency list but
// marked as "INONLY", while the twin is marked "OUTONLY" (by the same function).
while (gp_IsArc(adjList))
{
e = adjList;
gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), NIL);
if ((adjList = gp_GetNextArc(theGraph, e)) == e)
adjList = NIL;
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e));
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e));
gp_AttachFirstArc(theGraph, v, e);
gp_SetDirection(theGraph, e, EDGEFLAG_DIRECTION_INONLY);
}
}
if (zeroBased)
theGraph->internalFlags |= FLAGS_ZEROBASEDIO;
return OK;
}
/********************************************************************
_ReadLEDAGraph()
Reads the edge list from a LEDA file containing a simple undirected graph.
LEDA files use a one-based numbering system, which is converted to
zero-based numbers if the graph reports starting at zero as the first vertex.
Returns: OK on success, NONEMBEDDABLE if success except too many edges
NOTOK on file content error (or internal error)
********************************************************************/
int _ReadLEDAGraph(graphP theGraph, FILE *Infile)
{
char Line[256];
int N, M, m, u, v, ErrorCode;
int zeroBasedOffset = gp_GetFirstVertex(theGraph)==0 ? 1 : 0;
/* Skip the lines that say LEDA.GRAPH and give the node and edge types */
fgets(Line, 255, Infile);
fgets(Line, 255, Infile);
fgets(Line, 255, Infile);
/* Read the number of vertices N, initialize the graph, then skip N. */
fgets(Line, 255, Infile);
sscanf(Line, " %d", &N);
if (gp_InitGraph(theGraph, N) != OK)
return NOTOK;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
fgets(Line, 255, Infile);
/* Read the number of edges */
fgets(Line, 255, Infile);
sscanf(Line, " %d", &M);
/* Read and add each edge, omitting loops and parallel edges */
for (m = 0; m < M; m++)
{
fgets(Line, 255, Infile);
sscanf(Line, " %d %d", &u, &v);
if (u != v && !gp_IsNeighbor(theGraph, u-zeroBasedOffset, v-zeroBasedOffset))
{
if ((ErrorCode = gp_AddEdge(theGraph, u-zeroBasedOffset, 0, v-zeroBasedOffset, 0)) != OK)
return ErrorCode;
}
}
if (zeroBasedOffset)
theGraph->internalFlags |= FLAGS_ZEROBASEDIO;
return OK;
}
/********************************************************************
gp_Read()
Opens the given file, determines whether it is in adjacency list or
matrix format based on whether the file start with N or just a number,
calls the appropriate read function, then closes the file and returns
the graph.
Digraphs and loop edges are not supported in the adjacency matrix format,
which is upper triangular.
In the adjacency list format, digraphs are supported. Loop edges are
ignored without producing an error.
Pass "stdin" for the FileName to read from the stdin stream
Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges
********************************************************************/
int gp_Read(graphP theGraph, char *FileName)
{
FILE *Infile;
char Ch;
int RetVal;
if (strcmp(FileName, "stdin") == 0)
Infile = stdin;
else if ((Infile = fopen(FileName, READTEXT)) == NULL)
return NOTOK;
Ch = (char) fgetc(Infile);
ungetc(Ch, Infile);
if (Ch == 'N')
RetVal = _ReadAdjList(theGraph, Infile);
else if (Ch == 'L')
RetVal = _ReadLEDAGraph(theGraph, Infile);
else RetVal = _ReadAdjMatrix(theGraph, Infile);
if (RetVal == OK)
{
void *extraData = NULL;
long filePos = ftell(Infile);
long fileSize;
fseek(Infile, 0, SEEK_END);
fileSize = ftell(Infile);
fseek(Infile, filePos, SEEK_SET);
if (filePos < fileSize)
{
extraData = malloc(fileSize - filePos + 1);
fread(extraData, fileSize - filePos, 1, Infile);
}
/*// Useful for quick debugging of IO extensibility
if (extraData == NULL)
printf("extraData == NULL\n");
else
{
char *extraDataString = (char *) extraData;
extraDataString[fileSize - filePos] = '\0';
printf("extraData = '%s'\n", extraDataString);
}
*/
if (extraData != NULL)
{
RetVal = theGraph->functions.fpReadPostprocess(theGraph, extraData, fileSize - filePos);
free((void *) extraData);
}
}
if (strcmp(FileName, "stdin") != 0)
fclose(Infile);
return RetVal;
}
int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize)
{
return OK;
}
/********************************************************************
_WriteAdjList()
For each vertex, we write its number, a colon, the list of adjacent vertices,
then a NIL. The vertices occupy the first N positions of theGraph. Each
vertex is also has indicators of the first and last adjacency nodes (arcs)
in its adjacency list.
Returns: NOTOK if either param is NULL; OK otherwise (after printing
adjacency list representation to Outfile).
********************************************************************/
int _WriteAdjList(graphP theGraph, FILE *Outfile)
{
int v, e;
int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0;
if (theGraph==NULL || Outfile==NULL) return NOTOK;
fprintf(Outfile, "N=%d\n", theGraph->N);
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
fprintf(Outfile, "%d:", v - zeroBasedOffset);
e = gp_GetLastArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY)
fprintf(Outfile, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset);
e = gp_GetPrevArc(theGraph, e);
}
// Write NIL at the end of the adjacency list (in zero-based I/O, NIL was -1)
fprintf(Outfile, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL);
}
return OK;
}
/********************************************************************
_WriteAdjMatrix()
Outputs upper triangular matrix representation capable of being
read by _ReadAdjMatrix()
Note: This routine does not support digraphs and will return an
error if a directed edge is found.
returns OK for success, NOTOK for failure
********************************************************************/
int _WriteAdjMatrix(graphP theGraph, FILE *Outfile)
{
int v, e, K;
char *Row = NULL;
if (theGraph != NULL)
Row = (char *) malloc((theGraph->N+1)*sizeof(char));
if (Row==NULL || theGraph==NULL || Outfile==NULL)
{
if (Row != NULL) free(Row);
return NOTOK;
}
fprintf(Outfile, "%d\n", theGraph->N);
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
for (K = gp_GetFirstVertex(theGraph); K <= v; K++)
Row[K - gp_GetFirstVertex(theGraph)] = ' ';
for (K = v+1; gp_VertexInRange(theGraph, v); K++)
Row[K - gp_GetFirstVertex(theGraph)] = '0';
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetDirection(theGraph, e) == EDGEFLAG_DIRECTION_INONLY)
return NOTOK;
if (gp_GetNeighbor(theGraph, e) > v)
Row[gp_GetNeighbor(theGraph, e) - gp_GetFirstVertex(theGraph)] = '1';
e = gp_GetNextArc(theGraph, e);
}
Row[theGraph->N] = '\0';
fprintf(Outfile, "%s\n", Row);
}
free(Row);
return OK;
}
/********************************************************************
********************************************************************/
char _GetEdgeTypeChar(graphP theGraph, int e)
{
char type = 'U';
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
type = 'C';
else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_FORWARD)
type = 'F';
else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
type = 'P';
else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK)
type = 'B';
else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_RANDOMTREE)
type = 'T';
return type;
}
/********************************************************************
********************************************************************/
char _GetVertexObstructionTypeChar(graphP theGraph, int v)
{
char type = 'U';
if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW)
type = 'X';
else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW)
type = 'x';
if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW)
type = 'Y';
else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW)
type = 'y';
return type;
}
/********************************************************************
_WriteDebugInfo()
Writes adjacency list, but also includes the type value of each
edge (e.g. is it DFS child arc, forward arc or back arc?), and
the L, A and DFSParent of each vertex.
********************************************************************/
int _WriteDebugInfo(graphP theGraph, FILE *Outfile)
{
int v, e, EsizeOccupied;
if (theGraph==NULL || Outfile==NULL) return NOTOK;
/* Print parent copy vertices and their adjacency lists */
fprintf(Outfile, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M);
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
fprintf(Outfile, "%d(P=%d,lA=%d,LowPt=%d,v=%d):",
v, gp_GetVertexParent(theGraph, v),
gp_GetVertexLeastAncestor(theGraph, v),
gp_GetVertexLowpoint(theGraph, v),
gp_GetVertexIndex(theGraph, v));
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
fprintf(Outfile, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e);
e = gp_GetNextArc(theGraph, e);
}
fprintf(Outfile, " %d\n", NIL);
}
/* Print any root copy vertices and their adjacency lists */
for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++)
{
if (!gp_VirtualVertexInUse(theGraph, v))
continue;
fprintf(Outfile, "%d(copy of=%d, DFS child=%d):",
v, gp_GetVertexIndex(theGraph, v),
gp_GetDFSChildFromRoot(theGraph, v));
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
fprintf(Outfile, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e);
e = gp_GetNextArc(theGraph, e);
}
fprintf(Outfile, " %d\n", NIL);
}
/* Print information about vertices and root copy (virtual) vertices */
fprintf(Outfile, "\nVERTEX INFORMATION\n");
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
fprintf(Outfile, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n",
v,
gp_GetVertexIndex(theGraph, v),
(gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)),
gp_GetFirstArc(theGraph, v),
gp_GetLastArc(theGraph, v));
}
for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++)
{
if (gp_VirtualVertexNotInUse(theGraph, v))
continue;
fprintf(Outfile, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n",
v,
gp_GetVertexIndex(theGraph, v),
(gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)),
gp_GetFirstArc(theGraph, v),
gp_GetLastArc(theGraph, v));
}
/* Print information about edges */
fprintf(Outfile, "\nEDGE INFORMATION\n");
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++)
{
if (gp_EdgeInUse(theGraph, e))
{
fprintf(Outfile, "E[%3d] neighbor=%3d, type=%c, next arc=%3d, prev arc=%3d\n",
e,
gp_GetNeighbor(theGraph, e),
_GetEdgeTypeChar(theGraph, e),
gp_GetNextArc(theGraph, e),
gp_GetPrevArc(theGraph, e));
}
}
return OK;
}
/********************************************************************
gp_Write()
Writes theGraph into the file.
Pass "stdout" or "stderr" to FileName to write to the corresponding stream
Pass WRITE_ADJLIST, WRITE_ADJMATRIX or WRITE_DEBUGINFO for the Mode
NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST
Returns NOTOK on error, OK on success.
********************************************************************/
int gp_Write(graphP theGraph, char *FileName, int Mode)
{
FILE *Outfile;
int RetVal;
if (theGraph == NULL || FileName == NULL)
return NOTOK;
if (strcmp(FileName, "nullwrite") == 0)
return OK;
if (strcmp(FileName, "stdout") == 0)
Outfile = stdout;
else if (strcmp(FileName, "stderr") == 0)
Outfile = stderr;
else if ((Outfile = fopen(FileName, WRITETEXT)) == NULL)
return NOTOK;
switch (Mode)
{
case WRITE_ADJLIST :
RetVal = _WriteAdjList(theGraph, Outfile);
break;
case WRITE_ADJMATRIX :
RetVal = _WriteAdjMatrix(theGraph, Outfile);
break;
case WRITE_DEBUGINFO :
RetVal = _WriteDebugInfo(theGraph, Outfile);
break;
default :
RetVal = NOTOK;
break;
}
if (RetVal == OK)
{
void *extraData = NULL;
long extraDataSize;
RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData, &extraDataSize);
if (extraData != NULL)
{
if (!fwrite(extraData, extraDataSize, 1, Outfile))
RetVal = NOTOK;
free(extraData);
}
}
if (strcmp(FileName, "stdout") == 0 || strcmp(FileName, "stderr") == 0)
fflush(Outfile);
else if (fclose(Outfile) != 0)
RetVal = NOTOK;
return RetVal;
}
/********************************************************************
_WritePostprocess()
By default, no additional information is written.
********************************************************************/
int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize)
{
return OK;
}
/********************************************************************
_Log()
When the project is compiled with LOGGING enabled, this method writes
a string to the file PLANARITY.LOG in the current working directory.
On first write, the file is created or cleared.
Call this method with NULL to close the log file.
********************************************************************/
void _Log(char *Str)
{
static FILE *logfile = NULL;
if (logfile == NULL)
{
if ((logfile = fopen("PLANARITY.LOG", WRITETEXT)) == NULL)
return;
}
if (Str != NULL)
{
fprintf(logfile, "%s", Str);
fflush(logfile);
}
else
fclose(logfile);
}
void _LogLine(char *Str)
{
_Log(Str);
_Log("\n");
}
static char LogStr[512];
char *_MakeLogStr1(char *format, int one)
{
sprintf(LogStr, format, one);
return LogStr;
}
char *_MakeLogStr2(char *format, int one, int two)
{
sprintf(LogStr, format, one, two);
return LogStr;
}
char *_MakeLogStr3(char *format, int one, int two, int three)
{
sprintf(LogStr, format, one, two, three);
return LogStr;
}
char *_MakeLogStr4(char *format, int one, int two, int three, int four)
{
sprintf(LogStr, format, one, two, three, four);
return LogStr;
}
char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five)
{
sprintf(LogStr, format, one, two, three, four, five);
return LogStr;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphIsolator.c 0000664 0000000 0000000 00000073140 12716155161 0024425 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#define GRAPHISOLATOR_C
#include "graph.h"
/* Imported functions */
extern void _ClearVisitedFlags(graphP);
extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
extern int _JoinBicomps(graphP theGraph);
extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R);
/* Private function declarations (exported within system) */
int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R);
int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex,
int *pAncestor, int *pDescendant);
int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex,
int *pDescendant);
int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor,
int SubtreeRoot, int *pDescendant);
int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
void _AddBackEdge(graphP theGraph, int ancestor, int descendant);
int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
int _InitializeIsolatorContext(graphP theGraph);
int _IsolateMinorA(graphP theGraph);
int _IsolateMinorB(graphP theGraph);
int _IsolateMinorC(graphP theGraph);
int _IsolateMinorD(graphP theGraph);
int _IsolateMinorE(graphP theGraph);
int _IsolateMinorE1(graphP theGraph);
int _IsolateMinorE2(graphP theGraph);
int _IsolateMinorE3(graphP theGraph);
int _IsolateMinorE4(graphP theGraph);
int _GetLeastAncestorConnection(graphP theGraph, int cutVertex);
int _MarkDFSPathsToDescendants(graphP theGraph);
int _AddAndMarkUnembeddedEdges(graphP theGraph);
/****************************************************************************
gp_IsolateKuratowskiSubgraph()
****************************************************************************/
int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R)
{
int RetVal;
/* A subgraph homeomorphic to K_{3,3} or K_5 will be isolated by using the visited
flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we
subsequently set visited on all edges and vertices in the homeomorph. */
_ClearVisitedFlags(theGraph);
/* Next, we determine which of the non-planarity Minors was encountered
and the principal bicomp on which the isolator will focus attention. */
if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK)
return NOTOK;
if (_InitializeIsolatorContext(theGraph) != OK)
return NOTOK;
/* Call the appropriate isolator */
if (theGraph->IC.minorType & MINORTYPE_A)
RetVal = _IsolateMinorA(theGraph);
else if (theGraph->IC.minorType & MINORTYPE_B)
RetVal = _IsolateMinorB(theGraph);
else if (theGraph->IC.minorType & MINORTYPE_C)
RetVal = _IsolateMinorC(theGraph);
else if (theGraph->IC.minorType & MINORTYPE_D)
RetVal = _IsolateMinorD(theGraph);
else if (theGraph->IC.minorType & MINORTYPE_E)
RetVal = _IsolateMinorE(theGraph);
else
RetVal = NOTOK;
/* Delete the unmarked edges and vertices, and return */
if (RetVal == OK)
RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph);
return RetVal;
}
/****************************************************************************
_InitializeIsolatorContext()
****************************************************************************/
int _InitializeIsolatorContext(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
/* Obtains the edges connecting X and Y to ancestors of the current vertex */
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE ||
_FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE)
return NOTOK;
/* For Minor B, we seek the last pertinent child biconnected component, which
is also future pertinent, and obtain the DFS child in its root edge.
This child is the subtree root containing vertices with connections to
both the current vertex and an ancestor of the current vertex. */
if (theGraph->IC.minorType & MINORTYPE_B)
{
int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w);
IC->uz = gp_GetVertexLowpoint(theGraph, SubtreeRoot);
if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE ||
_FindUnembeddedEdgeToSubtree(theGraph, IC->uz, SubtreeRoot, &IC->dz) != TRUE)
return NOTOK;
}
/* For all other minors, we obtain an unembedded connecting the current vertex to the
pertinent vertex W, and for minor E we collect the additional unembedded ancestor
connection for the future pertinent vertex Z. */
else
{
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
if (theGraph->IC.minorType & MINORTYPE_E)
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)
return NOTOK;
}
return OK;
}
/****************************************************************************
_IsolateMinorA(): Isolate a K3,3 homeomorph
****************************************************************************/
int _IsolateMinorA(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorB(): Isolate a K3,3 homeomorph
****************************************************************************/
int _IsolateMinorB(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux,IC->uy,IC->uz),
MAX3(IC->ux,IC->uy,IC->uz)) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorC(): Isolate a K3,3 homeomorph
****************************************************************************/
int _IsolateMinorC(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (gp_GetVertexObstructionType(theGraph, IC->px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW)
{
int highY = gp_GetVertexObstructionType(theGraph, IC->py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW
? IC->py : IC->y;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, highY) != OK)
return NOTOK;
}
else
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->r) != OK)
return NOTOK;
}
// Note: The x-y path is already marked, due to identifying the type of non-planarity minor
if (_MarkDFSPathsToDescendants(theGraph) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorD(): Isolate a K3,3 homeomorph
****************************************************************************/
int _IsolateMinorD(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
// Note: The x-y and v-z paths are already marked, due to identifying the type of non-planarity minor
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->y) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorE()
****************************************************************************/
int _IsolateMinorE(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
/* Minor E1: Isolate a K3,3 homeomorph */
if (IC->z != IC->w)
return _IsolateMinorE1(theGraph);
/* Minor E2: Isolate a K3,3 homeomorph */
if (IC->uz > MAX(IC->ux, IC->uy))
return _IsolateMinorE2(theGraph);
/* Minor E3: Isolate a K3,3 homeomorph */
if (IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy)
return _IsolateMinorE3(theGraph);
/* Minor E4: Isolate a K3,3 homeomorph */
else if (IC->x != IC->px || IC->y != IC->py)
return _IsolateMinorE4(theGraph);
/* Minor E: Isolate a K5 homeomorph */
// Note: The x-y path is already marked, due to identifying the type of non-planarity minor
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), IC->r) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorE1()
Reduce to Minor C if the vertex Z responsible for external activity
below the X-Y path does not equal the pertinent vertex W.
****************************************************************************/
int _IsolateMinorE1(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (gp_GetVertexObstructionType(theGraph, IC->z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW)
{
gp_ResetVertexObstructionType(theGraph, IC->px, VERTEX_OBSTRUCTIONTYPE_HIGH_RXW);
IC->x=IC->z; IC->ux=IC->uz; IC->dx=IC->dz;
}
else if (gp_GetVertexObstructionType(theGraph, IC->z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW)
{
gp_ResetVertexObstructionType(theGraph, IC->py, VERTEX_OBSTRUCTIONTYPE_HIGH_RYW);
IC->y=IC->z; IC->uy=IC->uz; IC->dy=IC->dz;
}
else return NOTOK;
// Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor,
// but the x-y path is also included in minor C, so we let it stay marked since the minor C
// isolator also assumes the x-y path has been marked by non-planarity minor type identification
IC->z = IC->uz = IC->dz = NIL;
theGraph->IC.minorType ^= MINORTYPE_E;
theGraph->IC.minorType |= (MINORTYPE_C|MINORTYPE_E1);
return _IsolateMinorC(theGraph);
}
/****************************************************************************
_IsolateMinorE2()
If uZ (which is the ancestor of v that is adjacent to Z) is a
descendant of both uY and uX, then we reduce to Minor A
****************************************************************************/
int _IsolateMinorE2(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
// Note: The x-y path was already marked, due to identifying E as the type of non-planarity minor,
// but we're reducing to Minor A, which does not include the x-y path, so the visited flags are
// cleared as a convenient, if somewhat wasteful, way to clear the marking on the x-y path
_ClearVisitedFlags(theGraph);
IC->v = IC->uz;
IC->dw = IC->dz;
IC->z = IC->uz = IC->dz = NIL;
theGraph->IC.minorType ^= MINORTYPE_E;
theGraph->IC.minorType |= (MINORTYPE_A|MINORTYPE_E2);
return _IsolateMinorA(theGraph);
}
/****************************************************************************
_IsolateMinorE3()
****************************************************************************/
int _IsolateMinorE3(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (IC->ux < IC->uy)
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->px) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK)
return NOTOK;
}
else
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK)
return NOTOK;
}
// Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor
if (theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), IC->r) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
theGraph->IC.minorType |= MINORTYPE_E3;
return OK;
}
/****************************************************************************
_IsolateMinorE4()
****************************************************************************/
int _IsolateMinorE4(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (IC->px != IC->x)
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->w) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK)
return NOTOK;
}
else
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->px) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->w, IC->r) != OK)
return NOTOK;
}
// Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor
if (theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz),
MAX3(IC->ux, IC->uy, IC->uz)) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
theGraph->IC.minorType |= MINORTYPE_E4;
return OK;
}
/****************************************************************************
_GetLeastAncestorConnection()
This function searches for an ancestor of the current vertex v adjacent by a
cycle edge to the given cutVertex or one of its DFS descendants appearing in
a separated bicomp. The given cutVertex is assumed to be future pertinent
such that either the leastAncestor or the lowpoint of a separated DFS child
is less than v. We obtain the minimum possible connection from the cutVertex
to an ancestor of v.
****************************************************************************/
int _GetLeastAncestorConnection(graphP theGraph, int cutVertex)
{
int child;
int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex);
child = gp_GetVertexFuturePertinentChild(theGraph, cutVertex);
while (gp_IsVertex(child))
{
if (gp_IsSeparatedDFSChild(theGraph, child) &&
ancestor > gp_GetVertexLowpoint(theGraph, child))
ancestor = gp_GetVertexLowpoint(theGraph, child);
child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child);
}
return ancestor;
}
/****************************************************************************
_FindUnembeddedEdgeToAncestor()
This function searches for an ancestor of the current vertex v adjacent by a
cycle edge to the given cutVertex or one of its DFS descendants appearing in
a separated bicomp.
The given cutVertex is assumed to be future pertinent such that either the
leastAncestor or the lowpoint of a separated DFS child is less than v.
We obtain the minimum possible connection from the cutVertex to an ancestor
of v, then compute the descendant accordingly.
Returns TRUE if found, FALSE otherwise.
****************************************************************************/
int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex,
int *pAncestor, int *pDescendant)
{
int child, foundChild;
int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex);
child = gp_GetVertexFuturePertinentChild(theGraph, cutVertex);
foundChild = NIL;
while (gp_IsVertex(child))
{
if (gp_IsSeparatedDFSChild(theGraph, child) &&
ancestor > gp_GetVertexLowpoint(theGraph, child))
{
ancestor = gp_GetVertexLowpoint(theGraph, child);
foundChild = child;
}
child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child);
}
*pAncestor = ancestor;
// If the least ancestor connection was direct, then return the cutVertex as the descendant
if (ancestor == gp_GetVertexLeastAncestor(theGraph, cutVertex))
{
*pDescendant = cutVertex;
return TRUE;
}
// Otherwise find the descendant based on the separated child with least lowpoint
return _FindUnembeddedEdgeToSubtree(theGraph, *pAncestor, foundChild, pDescendant);
}
/****************************************************************************
_FindUnembeddedEdgeToCurVertex()
Given the current vertex v, we search for an edge connecting v to either
a given pertinent vertex W or one of its DFS descendants in the subtree
indicated by the the last pertinent child biconnected component.
Returns TRUE if founds, FALSE otherwise.
****************************************************************************/
int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant)
{
if (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, cutVertex)))
{
*pDescendant = cutVertex;
return TRUE;
}
else
{
int subtreeRoot = gp_GetVertexFirstPertinentRootChild(theGraph, cutVertex);
return _FindUnembeddedEdgeToSubtree(theGraph, theGraph->IC.v,
subtreeRoot, pDescendant);
}
}
/****************************************************************************
_FindUnembeddedEdgeToSubtree()
Given the root vertex of a DFS subtree and an ancestor of that subtree,
find a vertex in the subtree that is adjacent to the ancestor by a
cycle edge.
Returns TRUE if found, FALSE if not found.
****************************************************************************/
int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor,
int SubtreeRoot, int *pDescendant)
{
int e, Z, ZNew;
*pDescendant = NIL;
/* If SubtreeRoot is a root copy, then we change to the DFS child in the
DFS tree root edge of the bicomp rooted by SubtreeRoot. */
SubtreeRoot = gp_IsVirtualVertex(theGraph, SubtreeRoot)
? gp_GetDFSChildFromRoot(theGraph, SubtreeRoot)
: SubtreeRoot;
/* Find the least descendant of the cut vertex incident to the ancestor. */
e = gp_GetVertexFwdArcList(theGraph, ancestor);
while (gp_IsArc(e))
{
if (gp_GetNeighbor(theGraph, e) >= SubtreeRoot)
{
if (gp_IsNotVertex(*pDescendant) || *pDescendant > gp_GetNeighbor(theGraph, e))
*pDescendant = gp_GetNeighbor(theGraph, e);
}
e = gp_GetNextArc(theGraph, e);
if (e == gp_GetVertexFwdArcList(theGraph, ancestor))
e = NIL;
}
if (gp_IsNotVertex(*pDescendant))
return FALSE;
/* Make sure the identified descendant actually descends from the cut vertex */
Z = *pDescendant;
while (Z != SubtreeRoot)
{
ZNew = gp_GetVertexParent(theGraph, Z);
if (gp_IsNotVertex(ZNew) || ZNew == Z)
return FALSE;
Z = ZNew;
}
/* Return successfully */
return TRUE;
}
/****************************************************************************
_MarkPathAlongBicompExtFace()
Sets the visited flags of vertices and edges on the external face of a
bicomp from startVert to endVert, inclusive, by following the 'first' arc
link out of each visited vertex.
****************************************************************************/
int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert)
{
int Z, ZPrevLink, ZPrevArc;
/* Mark the start vertex (and if it is a root copy, mark the parent copy too. */
gp_SetVertexVisited(theGraph, startVert);
/* For each vertex visited after the start vertex, mark the vertex and the
edge used to get there. Stop after marking the ending vertex. */
Z = startVert;
ZPrevLink = 1;
do {
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
ZPrevArc = gp_GetArc(theGraph, Z, ZPrevLink);
gp_SetEdgeVisited(theGraph, ZPrevArc);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, ZPrevArc));
gp_SetVertexVisited(theGraph, Z);
} while (Z != endVert);
return OK;
}
/****************************************************************************
_MarkDFSPath()
Sets visited flags of vertices and edges from descendant to ancestor,
including root copy vertices, and including the step of hopping from
a root copy to its parent copy.
At each vertex, the edge record is obtained whose type indicates that it
leads to the DFS parent. An earlier implementation just used the DFS parent
member of the vertex, but then had to find the edge to mark anyway.
This method is more generalized because some extension algorithms reduce
DFS paths to single DFS tree edges, in which case the edge record with type
EDGE_TYPE_PARENT may indicate the DFS paent or an ancestor.
****************************************************************************/
int _MarkDFSPath(graphP theGraph, int ancestor, int descendant)
{
int e, parent;
// If we are marking from a root (virtual) vertex upward, then go up to the parent
// copy before starting the loop
if (gp_IsVirtualVertex(theGraph, descendant))
descendant = gp_GetPrimaryVertexFromRoot(theGraph, descendant);
// Mark the lowest vertex (the one with the highest number).
gp_SetVertexVisited(theGraph, descendant);
// Mark all ancestors of the lowest vertex, and the edges used to reach
// them, up to the given ancestor vertex.
while (descendant != ancestor)
{
if (gp_IsNotVertex(descendant))
return NOTOK;
// If we are at a bicomp root, then ascend to its parent copy and
// mark it as visited.
if (gp_IsVirtualVertex(theGraph, descendant))
{
parent = gp_GetPrimaryVertexFromRoot(theGraph, descendant);
}
// If we are on a regular, non-virtual vertex then get the edge to the parent,
// mark the edge, then fall through to the code that marks the parent vertex.
else
{
// Scan the edges for the one marked as the DFS parent
parent = NIL;
e = gp_GetFirstArc(theGraph, descendant);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
{
parent = gp_GetNeighbor(theGraph, e);
break;
}
e = gp_GetNextArc(theGraph, e);
}
// Sanity check on the data structure integrity
if (gp_IsNotVertex(parent))
return NOTOK;
// Mark the edge
gp_SetEdgeVisited(theGraph, e);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
}
// Mark the parent, then hop to the parent and reiterate
gp_SetVertexVisited(theGraph, parent);
descendant = parent;
}
return OK;
}
/****************************************************************************
_MarkDFSPathsToDescendants()
****************************************************************************/
int _MarkDFSPathsToDescendants(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK)
return NOTOK;
if (gp_IsVertex(IC->dw))
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK)
return NOTOK;
if (gp_IsVertex(IC->dz))
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dz) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_AddAndMarkUnembeddedEdges()
****************************************************************************/
int _AddAndMarkUnembeddedEdges(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK ||
_AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK)
return NOTOK;
if (gp_IsVertex(IC->dw))
if (_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK)
return NOTOK;
if (gp_IsVertex(IC->dz))
if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_AddAndMarkEdge()
Adds edge records for the edge (ancestor, descendant) and marks the edge
records and vertex structures that represent the edge.
****************************************************************************/
int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant)
{
_AddBackEdge(theGraph, ancestor, descendant);
/* Mark the edge so it is not deleted */
gp_SetVertexVisited(theGraph, ancestor);
gp_SetEdgeVisited(theGraph, gp_GetFirstArc(theGraph, ancestor));
gp_SetEdgeVisited(theGraph, gp_GetFirstArc(theGraph, descendant));
gp_SetVertexVisited(theGraph, descendant);
return OK;
}
/****************************************************************************
_AddBackEdge()
This function transfers the edge records for the edge between the ancestor
and descendant from the forward edge list of the ancestor to the adjacency
lists of the ancestor and descendant.
****************************************************************************/
void _AddBackEdge(graphP theGraph, int ancestor, int descendant)
{
int fwdArc, backArc;
/* We get the two edge records of the back edge to embed. */
fwdArc = gp_GetVertexFwdArcList(theGraph, ancestor);
while (gp_IsArc(fwdArc))
{
if (gp_GetNeighbor(theGraph, fwdArc) == descendant)
break;
fwdArc = gp_GetNextArc(theGraph, fwdArc);
if (fwdArc == gp_GetVertexFwdArcList(theGraph, ancestor))
fwdArc = NIL;
}
if (gp_IsNotArc(fwdArc))
return;
backArc = gp_GetTwinArc(theGraph, fwdArc);
/* The forward arc is removed from the fwdArcList of the ancestor. */
if (gp_GetVertexFwdArcList(theGraph, ancestor) == fwdArc)
{
if (gp_GetNextArc(theGraph, fwdArc) == fwdArc)
gp_SetVertexFwdArcList(theGraph, ancestor, NIL);
else gp_SetVertexFwdArcList(theGraph, ancestor, gp_GetNextArc(theGraph, fwdArc));
}
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc));
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc));
/* The forward arc is added to the adjacency list of the ancestor. */
gp_SetPrevArc(theGraph, fwdArc, NIL);
gp_SetNextArc(theGraph, fwdArc, gp_GetFirstArc(theGraph, ancestor));
gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, ancestor), fwdArc);
gp_SetFirstArc(theGraph, ancestor, fwdArc);
/* The back arc is added to the adjacency list of the descendant. */
gp_SetPrevArc(theGraph, backArc, NIL);
gp_SetNextArc(theGraph, backArc, gp_GetFirstArc(theGraph, descendant));
gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, descendant), backArc);
gp_SetFirstArc(theGraph, descendant, backArc);
gp_SetNeighbor(theGraph, backArc, ancestor);
}
/****************************************************************************
_DeleteUnmarkedVerticesAndEdges()
For each vertex, traverse its adjacency list and delete all unvisited edges.
****************************************************************************/
int _DeleteUnmarkedVerticesAndEdges(graphP theGraph)
{
int v, e;
/* All of the forward and back arcs of all of the edge records
were removed from the adjacency lists in the planarity algorithm
preprocessing. We now put them back into the adjacency lists
(and we do not mark them), so they can be properly deleted below. */
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
while (gp_IsArc(e = gp_GetVertexFwdArcList(theGraph, v)))
_AddBackEdge(theGraph, v, gp_GetNeighbor(theGraph, e));
}
/* Now we delete all unmarked edges. We don't delete vertices from the
embedding, but the ones we should delete will become degree zero. */
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetEdgeVisited(theGraph, e))
e = gp_GetNextArc(theGraph, e);
else e = gp_DeleteEdge(theGraph, e, 0);
}
}
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK23Search.c 0000664 0000000 0000000 00000022511 12716155161 0024312 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graph.h"
/* Imported functions */
extern void _ClearVisitedFlags(graphP);
extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
extern int _JoinBicomps(graphP theGraph);
extern int _MarkHighestXYPath(graphP theGraph);
extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant);
extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);
extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant);
extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
extern int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R);
extern int _IsolateOuterplanarityObstructionA(graphP theGraph);
extern int _IsolateOuterplanarityObstructionB(graphP theGraph);
/* Private function declarations for K_{2,3} searching */
int _SearchForK23InBicomp(graphP theGraph, int v, int R);
int _IsolateOuterplanarityObstructionE1orE2(graphP theGraph);
int _IsolateOuterplanarityObstructionE3orE4(graphP theGraph);
/****************************************************************************
_SearchForK23InBicomp()
****************************************************************************/
int _SearchForK23InBicomp(graphP theGraph, int v, int R)
{
isolatorContextP IC = &theGraph->IC;
int X, Y, XPrevLink, YPrevLink;
/* Begin by determining whether minor A, B or E is detected */
if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK)
return NOTOK;
/* Minors A and B result in the desired K_{2,3} homeomorph,
so we isolate it and return NONEMBEDDABLE. */
if (theGraph->IC.minorType & (MINORTYPE_A|MINORTYPE_B))
{
_ClearVisitedFlags(theGraph);
if (theGraph->IC.minorType & MINORTYPE_A)
{
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
if (_IsolateOuterplanarityObstructionA(theGraph) != OK)
return NOTOK;
}
else if (theGraph->IC.minorType & MINORTYPE_B)
{
int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w);
if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE)
return NOTOK;
if (_IsolateOuterplanarityObstructionB(theGraph) != OK)
return NOTOK;
}
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* For minor E (a K_4) , we run the additional tests to see if a K_{2,3} is
entangled with the K_4. If not, then we return OK to indicate that
the outerplanarity embedder should proceed as if the K_4 had not
been found. */
/* If any vertices other than R, X, Y and W exist along the
external face, then we can obtain a K_{2,3} by minor E1 or E2 */
X = IC->x;
Y = IC->y;
XPrevLink = 1;
YPrevLink = 0;
if (IC->w != _GetNeighborOnExtFace(theGraph, X, &XPrevLink) ||
IC->w != _GetNeighborOnExtFace(theGraph, Y, &YPrevLink))
{
_ClearVisitedFlags(theGraph);
if (_IsolateOuterplanarityObstructionE1orE2(theGraph) != OK)
return NOTOK;
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* If X, Y or W make either a direct back edge connection or a
connection through a separated child bicomp to an ancestor of
the current vertex v, then we can obtain a K_{2,3} by minor
E3 or E4. Note that this question is query on X, Y and W is
equivalent to the planarity version of external activity. */
gp_UpdateVertexFuturePertinentChild(theGraph, X, v);
gp_UpdateVertexFuturePertinentChild(theGraph, Y, v);
gp_UpdateVertexFuturePertinentChild(theGraph, IC->w, v);
if (FUTUREPERTINENT(theGraph, X, v) ||
FUTUREPERTINENT(theGraph, Y, v) ||
FUTUREPERTINENT(theGraph, IC->w, v))
{
_ClearVisitedFlags(theGraph);
if (_IsolateOuterplanarityObstructionE3orE4(theGraph) != OK)
return NOTOK;
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* The extra cases for finding a K_{2,3} failed, so the bicomp rooted
by R is a separable subgraph of the input that is isomorphic
to K_4. So, we restore the original vertex orientation of
the bicomp (because it's polite, not because we really have to).
Then, we return OK to tell the outerplanarity embedder that it
can ignore this K_4 and keep processing. */
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateOuterplanarityObstructionE1orE2()
****************************************************************************/
int _IsolateOuterplanarityObstructionE1orE2(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
int XPrevLink = 1;
if (_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
/* Isolate E1 */
if (theGraph->IC.px != theGraph->IC.x)
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->w) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK)
return NOTOK;
}
else if (theGraph->IC.py != theGraph->IC.y)
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->w, IC->r) != OK)
return NOTOK;
}
/* Isolate E2 */
else if (IC->w != _GetNeighborOnExtFace(theGraph, IC->x, &XPrevLink))
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->y) != OK)
return NOTOK;
}
else
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->r) != OK)
return NOTOK;
}
/* Final bits are in common */
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateOuterplanarityObstructionE3orE4()
****************************************************************************/
int _IsolateOuterplanarityObstructionE3orE4(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
int u, d, XorY;
// Minor E3
gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.x, theGraph->IC.v);
gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.y, theGraph->IC.v);
if (FUTUREPERTINENT(theGraph, theGraph->IC.x, theGraph->IC.v) ||
FUTUREPERTINENT(theGraph, theGraph->IC.y, theGraph->IC.v))
{
if (_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.x, theGraph->IC.v);
if (FUTUREPERTINENT(theGraph, theGraph->IC.x, theGraph->IC.v))
XorY = theGraph->IC.x;
else XorY = theGraph->IC.y;
/* The cases of X future pertinent and Y future pertinent
are the same except for the bicomp external face marking
(because parameter order is important) */
if (XorY == theGraph->IC.x)
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK)
return NOTOK;
}
else
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK)
return NOTOK;
}
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
if (_FindUnembeddedEdgeToAncestor(theGraph, XorY, &u, &d) != TRUE)
return NOTOK;
if (theGraph->functions.fpMarkDFSPath(theGraph, u, IC->v) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, XorY, d) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkEdge(theGraph, u, d) != OK ||
_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK)
return NOTOK;
return OK;
}
/* Otherwise, isolate Minor E4 (reduce to minor A) */
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->w, &u, &d) != TRUE)
return NOTOK;
IC->v = u;
IC->dw = d;
return _IsolateOuterplanarityObstructionA(theGraph);
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK23Search.h 0000664 0000000 0000000 00000000650 12716155161 0024317 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_K23SEARCH_H
#define GRAPH_K23SEARCH_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphStructures.h"
#ifdef __cplusplus
extern "C" {
#endif
#define K23SEARCH_NAME "K23Search"
int gp_AttachK23Search(graphP theGraph);
int gp_DetachK23Search(graphP theGraph);
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK23Search.private.h 0000664 0000000 0000000 00000000646 12716155161 0025775 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_K23SEARCH_PRIVATE_H
#define GRAPH_K23SEARCH_PRIVATE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graph.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
// Overloaded function pointers
graphFunctionTable functions;
} K23SearchContext;
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK23Search_Extensions.c 0000664 0000000 0000000 00000021050 12716155161 0026526 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "graphK23Search.private.h"
#include "graphK23Search.h"
extern int _SearchForK23InBicomp(graphP theGraph, int v, int R);
extern int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts);
extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,
int *imageVerts, int maxNumImageVerts);
extern int _TestSubgraph(graphP theSubgraph, graphP theGraph);
/* Forward declarations of overloading functions */
int _K23Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R);
int _K23Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
int _K23Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
/* Forward declarations of functions used by the extension system */
void *_K23Search_DupContext(void *pContext, void *theGraph);
void _K23Search_FreeContext(void *);
/****************************************************************************
* K23SEARCH_ID - the variable used to hold the integer identifier for this
* extension, enabling this feature's extension context to be distinguished
* from other features' extension contexts that may be attached to a graph.
****************************************************************************/
int K23SEARCH_ID = 0;
/****************************************************************************
gp_AttachK23Search()
This function adjusts the graph data structure to attach the K2,3 search
feature.
****************************************************************************/
int gp_AttachK23Search(graphP theGraph)
{
K23SearchContext *context = NULL;
// If the K2,3 search feature has already been attached to the graph
// then there is no need to attach it again
gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context);
if (context != NULL)
{
return OK;
}
// Allocate a new extension context
context = (K23SearchContext *) malloc(sizeof(K23SearchContext));
if (context == NULL)
{
return NOTOK;
}
// Put the overload functions into the context function table.
// gp_AddExtension will overload the graph's functions with these, and
// return the base function pointers in the context function table
memset(&context->functions, 0, sizeof(graphFunctionTable));
context->functions.fpHandleBlockedBicomp = _K23Search_HandleBlockedBicomp;
context->functions.fpEmbedPostprocess = _K23Search_EmbedPostprocess;
context->functions.fpCheckEmbeddingIntegrity = _K23Search_CheckEmbeddingIntegrity;
context->functions.fpCheckObstructionIntegrity = _K23Search_CheckObstructionIntegrity;
// Store the K23 search context, including the data structure and the
// function pointers, as an extension of the graph
if (gp_AddExtension(theGraph, &K23SEARCH_ID, (void *) context,
_K23Search_DupContext, _K23Search_FreeContext,
&context->functions) != OK)
{
_K23Search_FreeContext(context);
return NOTOK;
}
return OK;
}
/********************************************************************
gp_DetachK23Search()
********************************************************************/
int gp_DetachK23Search(graphP theGraph)
{
return gp_RemoveExtension(theGraph, K23SEARCH_ID);
}
/********************************************************************
_K23Search_DupContext()
********************************************************************/
void *_K23Search_DupContext(void *pContext, void *theGraph)
{
K23SearchContext *context = (K23SearchContext *) pContext;
K23SearchContext *newContext = (K23SearchContext *) malloc(sizeof(K23SearchContext));
if (newContext != NULL)
{
*newContext = *context;
}
return newContext;
}
/********************************************************************
_K23Search_FreeContext()
********************************************************************/
void _K23Search_FreeContext(void *pContext)
{
free(pContext);
}
/********************************************************************
********************************************************************/
int _K23Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R)
{
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23)
{
// If R is the root of a descendant bicomp of v, we push it, but then we know the search for K2,3
// will be successful and return NONEMBEDDABLE because this condition corresponds to minor A, which
// is a K2,3. Thus, an "OK to proceed with Walkdown searching elsewhere" result cannot happen,
// so we don't have to test for it to detect if we have to pop these two back off the stack.
if (R != RootVertex)
sp_Push2(theGraph->theStack, R, 0);
// The possible results here are NONEMBEDDABLE if a K2,3 homeomorph is found, or OK if only
// a K4 was found and unblocked such that it is OK for the Walkdown to continue searching
// elsewhere. Note that the OK result can only happen if RootVertex==R since minor E can only
// happen on a child bicomp of vertex v, not a descendant bicomp.
return _SearchForK23InBicomp(theGraph, v, R);
}
else
{
K23SearchContext *context = NULL;
gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K23Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
{
// For K2,3 search, we just return the edge embedding result because the
// search result has been obtained already.
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23)
{
return edgeEmbeddingResult;
}
// When not searching for K2,3, we let the superclass do the work
else
{
K23SearchContext *context = NULL;
gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K23Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
{
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23)
{
return OK;
}
// When not searching for K2,3, we let the superclass do the work
else
{
K23SearchContext *context = NULL;
gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
{
// When searching for K2,3, we ensure that theGraph is a subgraph of
// the original graph and that it contains a K2,3 homeomorph
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23)
{
int degrees[4], imageVerts[5];
if (_TestSubgraph(theGraph, origGraph) != TRUE)
return NOTOK;
if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK)
return NOTOK;
if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE)
{
return OK;
}
return NOTOK;
}
// When not searching for K2,3, we let the superclass do the work
else
{
K23SearchContext *context = NULL;
gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
}
}
return NOTOK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK33Search.c 0000664 0000000 0000000 00000240625 12716155161 0024323 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphK33Search.h"
#include "graphK33Search.private.h"
//extern int K33SEARCH_ID;
#include "graph.h"
/* Imported functions */
//extern void _ClearVisitedFlags(graphP);
extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph);
extern int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue);
//extern int _GetBicompSize(graphP theGraph, int BicompRoot);
extern int _HideInternalEdges(graphP theGraph, int vertex);
extern int _RestoreInternalEdges(graphP theGraph, int stackBottom);
extern int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot);
extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType);
extern int _SetEdgeType(graphP theGraph, int u, int v);
extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
extern int _JoinBicomps(graphP theGraph);
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
extern int _OrientVerticesInEmbedding(graphP theGraph);
//extern void _InvertVertex(graphP theGraph, int V);
extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x);
extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x);
extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x);
extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R);
extern int _MarkHighestXYPath(graphP theGraph);
extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R);
extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex);
extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);
extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant);
extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
extern int _IsolateMinorE1(graphP theGraph);
//extern int _IsolateMinorE2(graphP theGraph);
extern int _IsolateMinorE3(graphP theGraph);
extern int _IsolateMinorE4(graphP theGraph);
extern int _MarkDFSPathsToDescendants(graphP theGraph);
extern int _AddAndMarkUnembeddedEdges(graphP theGraph);
extern void _K33Search_InitEdgeRec(K33SearchContext *context, int e);
/* Private functions for K_{3,3} searching. */
int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R);
int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context);
int _SearchForMinorE1(graphP theGraph);
int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context);
int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max);
int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex);
int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int vertex,
int closerAncestor, int fartherAncestor);
int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor,
int cutVertex, int *pDescendant);
int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker);
int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker);
int _TestForLowXYPath(graphP theGraph);
int _TestForZtoWPath(graphP theGraph);
int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max);
int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot);
int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink);
int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R);
int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType);
int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType);
int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e);
int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context);
int _IsolateMinorE5(graphP theGraph);
int _IsolateMinorE6(graphP theGraph, K33SearchContext *context);
int _IsolateMinorE7(graphP theGraph, K33SearchContext *context);
/****************************************************************************
_SearchForK33InBicomp()
****************************************************************************/
int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R)
{
isolatorContextP IC = &theGraph->IC;
int tempResult;
/* Begin by determining which non-planarity minor is detected */
if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK)
return NOTOK;
/* If minor A is selected, then the root of the oriented bicomp has been changed */
else R = IC->r;
/* Minors A to D result in the desired K_{3,3} homeomorph,
so we isolate it and return NONEMBEDDABLE. */
if (theGraph->IC.minorType & (MINORTYPE_A|MINORTYPE_B|MINORTYPE_C|MINORTYPE_D))
{
/* First we restore the orientations of the vertices in the
one bicomp we have messed with so that there is no confusion. */
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
return NOTOK;
/* Next we restore the orientation of the embedding so we
can restore the reduced paths (because we avoid modifying
the Kuratowski subgraph isolator to restore reduced paths,
which are a construct of the K_{3,3} search). */
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
/* Next we simply call the Kuratowski subgraph isolation since
we know now that it will isolate a K_{3,3}.
For minor A, we need to set up the stack that would be
available immediately after a Walkdown failure. */
if (theGraph->IC.minorType & MINORTYPE_A)
{
sp_ClearStack(theGraph->theStack);
sp_Push2(theGraph->theStack, R, NIL);
}
if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* For minor E (a K5 minor), we run the additional tests to see if minors E1 to E4 apply
since these minors isolate a K_{3,3} entangled with the K5.
This is the key location where GetLeastAncestorConnection() must be constant time. */
IC->ux = _Fast_GetLeastAncestorConnection(theGraph, context, IC->x);
IC->uy = _Fast_GetLeastAncestorConnection(theGraph, context, IC->y);
IC->uz = _Fast_GetLeastAncestorConnection(theGraph, context, IC->z);
if (IC->z != IC->w ||
IC->uz > MAX(IC->ux, IC->uy) ||
(IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) ||
(IC->x != IC->px || IC->y != IC->py))
{
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
return NOTOK;
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* If the Kuratowski subgraph isolator will not isolate a K_{3,3} based on minor E,
then a K5 homeomorph could be isolated. However, a K_{3,3} may still be tangled
with the K5, so we now run the additional tests of the K_{3,3} search algorithm.
If the search finds a K_{3,3} (tempResult of NONEMBEDDABLE), then we remove unwanted
edges from the graph and return NONEMBEDDABLE. If the search has a fault (NOTOK),
then we return. If the result is OK, then a K_{3,3} was not found at this time
and we proceed with some clean-up work below. */
if ((tempResult = _RunExtraK33Tests(theGraph, context)) != OK)
{
if (tempResult == NONEMBEDDABLE)
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
return tempResult;
}
/* The extra cases for finding a K_{3,3} did not succeed, so the bicomp rooted by R
is either a K5 homeomorph (with at most a superficially entangled K_{3,3}) or
we have made the special setting that allows us to detect the one "merge blocker"
case that would be too costly to try now. Either way, we can safely reduce the
bicomp to the 4-cycle (R, X, W, Y, R) and proceed with the planarity algorithm.
We also restore the mixed orientation of the bicomp (i.e. the proper
orientation in the context of the edge signs) because this code can work
when ReduceBicomp doesn't do any actual work. */
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
return NOTOK;
if (_ReduceBicomp(theGraph, context, R) != OK)
return NOTOK;
/* Set visitedInfo values in the bicomp to the initialized state so the planarity
algorithm can properly do the Walkup procedure in future steps */
if (_FillVertexVisitedInfoInBicomp(theGraph, IC->r, theGraph->N) != OK)
return NOTOK;
/* We now intend to ignore the pertinence of W (conceptually eliminating
the connection from W to the current vertex). Note that none of the
bicomp roots in the pertinentRootsList (nor their respective subtrees)
will be visited again by the planarity algorithm because they must've
been only pertinent. If they were future pertinent and pertinent,
then we would've found a K_{3,3} by non-planarity minor B. Thus, the original
Walkup costs that identified the pertinent bicomps we intend to ignore are
one-time costs, preserving linear time. */
gp_SetVertexPertinentEdge(theGraph, IC->w, NIL);
gp_SetVertexPertinentRootsList(theGraph, IC->w, NIL);
return OK;
}
/****************************************************************************
_RunExtraK33Tests()
****************************************************************************/
#define USE_MERGEBLOCKER
int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context)
{
isolatorContextP IC = &theGraph->IC;
int u_max = MAX3(IC->ux, IC->uy, IC->uz);
#ifndef USE_MERGEBLOCKER
int u;
#endif
/* Case 1: If there is a pertinent or future pertinent vertex other than W
on the lower external face path between X and Y (the points of
attachment of the x-y path), then we can isolate a K_{3,3} homeomorph
by Minor E1. */
if (_SearchForMinorE1(theGraph) != OK)
return NOTOK;
if (IC->w != IC->z)
{
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE1(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* Case 2: If W/Z can make an external connection to an ancestor of V
that is descendant to u_{max}, then a K_{3,3} homeomorph can
be isolated with Minor E2.
OPTIMIZATION: We do not need to check for this case.
We avoid doing so because in very specially crafted cases
it could be too costly if the connection doesn't exist.
However, if the highest numbered ancestor H of the current vertex
that has an external connection from W is a descendant u_{max}
then we will discover a K_{3,3} by Minor A or B in step H
(unless some other test succeeds at finding a K_{3,3} first),
so we just let the non-planarity detector do its work since
Minors A and B both provide a K_{3,3} when found.
This happens because w is pertinent to H and future pertinent
to u_max or an ancestor of u_max.
Minor A will happen if, in step H, Walkdown descends to the
bicomp containing the current vertex, x, y and w. Since x
and y would still be future pertinent (they connect to u_max
or higher, i.e. with lesser DFI, than u_max).
Minor B will happen if the bicomp containing the current vertex,
x, y and w is a descendant of a bicomp that blocks planarity
in step H. The bicomp would be both pertinent (due to w's
connection to H) and future pertinent(due to connections to
ancestors of H by w, x and y).
*/
#ifndef USE_MERGEBLOCKER
u = _SearchForDescendantExternalConnection(theGraph, context, IC->w, u_max);
if (u > u_max)
{
IC->uz = u;
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE2(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
#endif
/* Case 3: If X or Y can make an external connection to an ancestor of V
that is descendant to u_{max}, then a K_{3,3} homeomorph
can be isolated with Minor E3.
NOTE: Due to the prior use of the Kuratowski subgraph
isolator, we know that at most one of X, Y or W/Z could have an
external connection to an ancestor of u_{max} = MAX(ux, uy, uz).
OPTIMIZATION: We do not check for the lower connection required
to find Minor E3 because it might ultimately be too costly.
Instead, we mark the vertex with a 'merge blocker' of u_{max}.
If the planar embedder attempts to merge the vertex prior to step
u_{max}, then the embedder has found the desired connection and a
K_{3,3} homeomorph is isolated at that time.
*/
#ifdef USE_MERGEBLOCKER
context->VI[IC->x].mergeBlocker = u_max;
#endif
#ifndef USE_MERGEBLOCKER
u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max);
if (u > u_max)
{
IC->ux = u;
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE3(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
#endif
#ifdef USE_MERGEBLOCKER
context->VI[IC->y].mergeBlocker = u_max;
#endif
#ifndef USE_MERGEBLOCKER
u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max);
if (u > u_max)
{
IC->uy = u;
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE3(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
#endif
/* Case 4: If there exists any x-y path with points of attachment px and py
such that px!=x or py!=y, then a K_{3,3} homeomorph can be isolated
with Minor E4. */
if (_TestForLowXYPath(theGraph) != OK)
return NOTOK;
if (IC->px != IC->x || IC->py != IC->y)
{
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE4(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* Case 5: If the x-y path contains an internal vertex that starts a second
internal path from the internal vertex to W/Z, then a K_{3,3} homeomorph
can be isolated with Minor E5. */
if (_TestForZtoWPath(theGraph) != OK)
return NOTOK;
if (gp_GetVertexVisited(theGraph, IC->w))
{
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE5(theGraph) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
/* Case 6: If uz < u_{max} and there is an external connection (other than external
connections involving X, Y and W/Z) between an ancestor of u_{max} and a
vertex in the range [V...u_{max}), then a K_{3,3} homeomorph can be
isolated with Minor E6.
OPTIMIZATION: See _TestForStraddlingBridge() */
if (IC->uz < u_max)
{
if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max)))
{
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE6(theGraph, context) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
}
/* Case 7: If ux < u_{max} or uy < u_{max} and there is an external connection
between an ancestor of u_{max} and a vertex in the range [V...u_{max})
(except for external connections involving X, Y and W/Z), then a K_{3,3}
homeomorph can be isolated with Minor E7.
OPTIMIZATION: Same as Case 6.*/
if (IC->ux < u_max || IC->uy < u_max)
{
if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max)))
{
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE7(theGraph, context) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
}
/* If none of the tests found a K_{3,3}, then we return OK to indicate that nothing
went wrong, but a K_{3,3} was not found. */
return OK;
}
/****************************************************************************
_SearchForMinorE1()
Search along the external face below the x-y path for a vertex Z other
than W that is future pertinent or pertinent.
****************************************************************************/
int _SearchForMinorE1(graphP theGraph)
{
int Z=theGraph->IC.px, ZPrevLink=1;
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
while (Z != theGraph->IC.py)
{
if (Z != theGraph->IC.w)
{
gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v);
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
{
theGraph->IC.z = Z;
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
return OK;
}
else if (PERTINENT(theGraph, Z))
{
/* Swap the roles of W and Z */
theGraph->IC.z = theGraph->IC.w;
theGraph->IC.w = Z;
/* If the new W (indicated by Z) was on the path (R, X, old W) then
the new Z (the old W, which has no type mark) is on the path
(X, new W, new Z, Y) so we change the type new Z to being on the
RYW path. Otherwise, the order is (X, new Z, new W, Y), so the
new Z (old W with no type) is type changed to be on the RXW path.*/
if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW)
gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RYW);
else gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RXW);
/* For completeness, we change the new W to type unknown */
gp_ClearVertexObstructionType(theGraph, theGraph->IC.w);
/* The external activity ancestor connection of the new Z must be obtained */
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, theGraph->IC.z);
return OK;
}
}
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
return OK;
}
/****************************************************************************
_FinishIsolatorContextInitialization()
Once it has been decided that a desired subgraph can be isolated, it
becomes safe to finish the isolator context initialization.
****************************************************************************/
int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context)
{
isolatorContextP IC = &theGraph->IC;
/* Restore the orientation of the bicomp on which we're working, then
perform orientation of all vertices in graph. (An unnecessary but
polite step that simplifies the description of key states of the
data structures). */
if (_OrientVerticesInBicomp(theGraph, IC->r, 1) != OK)
return NOTOK;
if (_OrientVerticesInEmbedding(theGraph) != OK)
return NOTOK;
/* Restore any paths that were reduced to single edges */
if (_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
/* We assume that the current bicomp has been marked appropriately,
but we must now clear the visitation flags of all other bicomps. */
if (_ClearVisitedFlagsInOtherBicomps(theGraph, IC->r) != OK)
return NOTOK;
/* To complete the normal behavior of _ClearVisitedFlags() in the
normal isolator context initialization, we also have to clear
the visited flags on all edges that have not yet been embedded */
_ClearVisitedFlagsInUnembeddedEdges(theGraph);
/* Now we can find the descendant ends of unembedded back edges based on
the ancestor settings ux, uy and uz. */
if (_FindExternalConnectionDescendantEndpoint(theGraph, IC->ux, IC->x, &IC->dx) != OK ||
_FindExternalConnectionDescendantEndpoint(theGraph, IC->uy, IC->y, &IC->dy) != OK ||
_FindExternalConnectionDescendantEndpoint(theGraph, IC->uz, IC->z, &IC->dz) != OK)
return NOTOK;
/* Finally, we obtain the descendant end of an unembedded back edge to
the current vertex. */
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
return OK;
}
/****************************************************************************
_Fast_GetLeastAncestorConnection()
This function searches for an ancestor of the current vertex v adjacent by a
cycle edge to the given cutVertex or one of its DFS descendants appearing in
a separated bicomp. The given cutVertex is assumed to be future pertinent
such that either the leastAncestor or the lowpoint of a separated DFS child
is less than v. We obtain the minimum possible connection from the cutVertex
to an ancestor of v.
This function performs the same operation as _GetLeastAncestorConnection(),
except in constant time.
****************************************************************************/
int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex)
{
int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex);
int child = context->VI[cutVertex].separatedDFSChildList;
if (gp_IsVertex(child) && ancestor > gp_GetVertexLowpoint(theGraph, child))
ancestor = gp_GetVertexLowpoint(theGraph, child);
return ancestor;
}
/****************************************************************************
_GetAdjacentAncestorInRange()
Returns the ancestor of theVertex that is adjacent to theVertex by an
unembedded back edge and has a DFI strictly between closerAncestor and
fartherAncestor.
Returns NIL if theVertex has no such neighboring ancestor.
****************************************************************************/
int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int theVertex,
int closerAncestor, int fartherAncestor)
{
int e = context->VI[theVertex].backArcList;
while (gp_IsArc(e))
{
if (gp_GetNeighbor(theGraph, e) < closerAncestor &&
gp_GetNeighbor(theGraph, e) > fartherAncestor)
return gp_GetNeighbor(theGraph, e);
e = gp_GetNextArc(theGraph, e);
if (e == context->VI[theVertex].backArcList)
e = NIL;
}
return NIL;
}
/****************************************************************************
_SearchForDescendantExternalConnection()
Search the cutVertex and each separated child subtree for an external
connection to a vertex ancestor to the current vertex V and descendant to u_max.
The function returns the descendant of u_max found to have an external
connection to the given cut vertex.
****************************************************************************/
int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max)
{
isolatorContextP IC = &theGraph->IC;
int u2 = _GetAdjacentAncestorInRange(theGraph, context, cutVertex, IC->v, u_max);
int child, descendant;
// Test cutVertex for an external connection to descendant of u_max via direct back edge
if (gp_IsVertex(u2))
return u2;
// If there is no direct back edge connection from the cut vertex
// to a vertex on the path between V and u_max, then we will
// look for such a connection in the DFS subtrees rooted by
// separated DFS children of the vertex (ignoring those whose
// lowpoint indicates that they make no external connections)
// Begin by pushing the separated DFS children of the cut vertex with
// lowpoints indicating connections to ancestors of the current vertex.
sp_ClearStack(theGraph->theStack);
child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex);
while (gp_IsVertex(child))
{
if (gp_GetVertexLowpoint(theGraph, child) < IC->v && gp_IsSeparatedDFSChild(theGraph, child))
sp_Push(theGraph->theStack, child);
child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child);
}
// Now process the stack until it is empty or until we've found the desired connection.
while (!sp_IsEmpty(theGraph->theStack))
{
sp_Pop(theGraph->theStack, descendant);
// If the vertex has a lowpoint indicating that it makes no external connections,
// then skip the subtree rooted by the vertex
if (gp_GetVertexLowpoint(theGraph, descendant) < IC->v)
{
// Check the subtree root for the desired connection.
u2 = _GetAdjacentAncestorInRange(theGraph, context, descendant, IC->v, u_max);
if (gp_IsVertex(u2))
return u2;
// Push each child as a new subtree root to be considered, except skip those whose lowpoint is too great.
child = gp_GetVertexSortedDFSChildList(theGraph, descendant);
while (gp_IsVertex(child))
{
if (gp_GetVertexLowpoint(theGraph, child) < IC->v)
sp_Push(theGraph->theStack, child);
child = gp_GetVertexNextDFSChild(theGraph, descendant, child);
}
}
}
// The only external connections from the cutVertex lead to u_max, so return it.
return u_max;
}
/****************************************************************************
_FindExternalConnectionDescendantEndpoint()
This operation is similar to _FindUnembeddedEdgeToAncestor() except that
we need to be more precise in this case, finding an external connection
from a given cut vertex to a *particular* given ancestor.
NOTE: By external we don't mean externall active so much as not embedded in
the bicomp containing the cut vertex.
Returns OK if it finds that either the given cutVertex or one of its
descendants in a separated bicomp has an unembedded back edge
connection to the given ancestor vertex.
Returns NOTOK otherwise (it is an error to not find the descendant because
this function is only called if _SearchForDescendantExternalConnection()
has already determined the existence of the descendant).
****************************************************************************/
int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor,
int cutVertex, int *pDescendant)
{
int child, e;
// Check whether the cutVertex is directly adjacent to the ancestor
// by an unembedded back edge.
e = gp_GetVertexFwdArcList(theGraph, ancestor);
while (gp_IsArc(e))
{
if (gp_GetNeighbor(theGraph, e) == cutVertex)
{
*pDescendant = cutVertex;
return OK;
}
e = gp_GetNextArc(theGraph, e);
if (e == gp_GetVertexFwdArcList(theGraph, ancestor))
e = NIL;
}
// Now check the descendants of the cut vertex to see if any make
// a connection to the ancestor.
child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex);
while (gp_IsVertex(child))
{
if (gp_GetVertexLowpoint(theGraph, child) < theGraph->IC.v && gp_IsSeparatedDFSChild(theGraph, child))
{
if (_FindUnembeddedEdgeToSubtree(theGraph, ancestor, child, pDescendant) == TRUE)
return OK;
}
child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child);
}
return NOTOK;
}
/****************************************************************************
_SearchForMergeBlocker()
This function helps to implement the merge blocking optimization of
_SearchForDescendantExternalConnection(). The function RunExtraK33Tests()
sets a mergeBlocker rather than run _SearchForDescendantExternalConnection()
in certain cases. This procedure is called by MergeBicomps to test the
embedding stack for a merge blocker before merging any biconnected components.
If a merge blocker is found, then FindK33WithMergeBlocker() is called and
ultimately the embedder's Walkdown function is terminated since a K_{3,3}
is isolated.
Returns OK on success (whether or not the search found a merge blocker)
NOTOK on internal function failure
pMergeBlocker is set to NIL unless a merge blocker is found.
****************************************************************************/
int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker)
{
stackP tempStack;
int R, Rout, Z, ZPrevLink;
/* Set return result to 'not found' then return if there is no stack to inspect */
*pMergeBlocker = NIL;
if (sp_IsEmpty(theGraph->theStack))
return OK;
/* Create a copy of the embedding stack */
tempStack = sp_Duplicate(theGraph->theStack);
if (tempStack == NULL)
return NOTOK;
/* Search the copy of the embedding stack for a merge blocked vertex */
while (!sp_IsEmpty(tempStack))
{
sp_Pop2(tempStack, R, Rout);
sp_Pop2(tempStack, Z, ZPrevLink);
if (gp_IsVertex(context->VI[Z].mergeBlocker) &&
context->VI[Z].mergeBlocker < v)
{
*pMergeBlocker = Z;
break;
}
}
sp_Free(&tempStack);
return OK;
}
/****************************************************************************
_FindK33WithMergeBlocker()
This function completes the merge blocking optimization by isolating a K_{3,3}
based on minor E3 if a merge blocked vertex was previously found.
Returns OK on success, NOTOK on internal function failure
****************************************************************************/
int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker)
{
int R, RPrevLink, u_max, u, e, W;
isolatorContextP IC = &theGraph->IC;
/* First, we orient the vertices so we can successfully restore all of the
reduced paths. This needs to be done before reconstructing the context
for CASE 3 of RunExtraK33Tests() because the reconstruction involves
using the Walkup to v from a descendant of v, which will not work if
the descendant is in one of the reduced paths. */
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
/* Reconstruct the context that was present for CASE 3 of RunExtraK33Tests()
when we decided to set a mergeBlocker rather than calling
_SearchForDescendantExternalConnection() */
/* Obtain the root of the bicomp containing the mergeBlocker. */
RPrevLink = 1;
R = mergeBlocker;
while (gp_IsNotVirtualVertex(theGraph, R))
R = _GetNeighborOnExtFace(theGraph, R, &RPrevLink);
/* Switch the 'current step' variable v to be equal to the
non-virtual counterpart of the bicomp root. */
IC->v = gp_GetPrimaryVertexFromRoot(theGraph, R);
/* Reinitialize the visitation, pertinence and future pertinence settings from step u_max for step v */
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
gp_SetVertexVisitedInfo(theGraph, v, theGraph->N);
gp_SetVertexPertinentEdge(theGraph, v, NIL);
gp_SetVertexPertinentRootsList(theGraph, v, NIL);
// Any calls to actually determine FUTUREPERTINENT status for a vertex w will actually invoke
// gp_UpdateVertexFuturePertinentChild(theGraph, w, v) beforehand, so only need to reinitialize here
gp_SetVertexFuturePertinentChild(theGraph, v, gp_GetVertexSortedDFSChildList(theGraph, v));
}
/* Restore the pertinence settings of step v by doing the Walkup for each
back edge that was not embedded when step v was originally performed. */
e = gp_GetVertexFwdArcList(theGraph, IC->v);
while (gp_IsArc(e))
{
W = gp_GetNeighbor(theGraph, e);
theGraph->functions.fpWalkUp(theGraph, IC->v, e);
e = gp_GetNextArc(theGraph, e);
if (e == gp_GetVertexFwdArcList(theGraph, IC->v))
e = NIL;
}
/* Next, we make the standard initialization calls for when we have found
a non-planarity condition. */
sp_ClearStack(theGraph->theStack);
if (_ChooseTypeOfNonplanarityMinor(theGraph, IC->v, R) != OK)
return NOTOK;
IC->ux = _GetLeastAncestorConnection(theGraph, IC->x);
IC->uy = _GetLeastAncestorConnection(theGraph, IC->y);
IC->uz = _GetLeastAncestorConnection(theGraph, IC->z);
u_max = MAX3(IC->ux,IC->uy,IC->uz);
/* Perform the remainder of CASE 3 of RunExtraK33Tests() */
if (mergeBlocker == IC->x)
{
u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max);
if (u > u_max)
{
IC->ux = u;
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE3(theGraph) != OK)
return NOTOK;
}
else return NOTOK;
}
else if (mergeBlocker == IC->y)
{
u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max);
if (u > u_max)
{
IC->uy = u;
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
_IsolateMinorE3(theGraph) != OK)
return NOTOK;
}
else return NOTOK;
}
else return NOTOK;
/* Do the final clean-up to obtain the K_{3,3} */
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_TestForLowXYPath()
Is there an x-y path that does not include X?
If not, is there an x-y path that does not include Y?
If not, then we restore the original x-y path.
If such a low x-y path exists, then we adjust px or py accordingly,
and we make sure that X or Y (whichever is excluded) and its edges are
not marked visited.
This method uses the stack, though it is called with an empty stack currently,
it does happen to preserve any preceding stack content. This method pushes
at most one integer per edge incident to the bicomp root plus two integers
per vertex in the bicomp.
****************************************************************************/
int _TestForLowXYPath(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
int result;
int stackBottom;
/* Clear the previously marked X-Y path */
if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK)
return NOTOK;
/* Save the size of the stack before hiding any edges, so we will know
how many edges to restore */
stackBottom = sp_GetCurrentSize(theGraph->theStack);
/* Hide the internal edges of X */
if (_HideInternalEdges(theGraph, IC->x) != OK)
return NOTOK;
/* Try to find a low X-Y path that excludes X, then restore the
internal edges of X. */
result = _MarkHighestXYPath(theGraph);
if (_RestoreInternalEdges(theGraph, stackBottom) != OK)
return NOTOK;
/* If we found the low X-Y path, then return. */
if (result == TRUE)
return OK;
/* Hide the internal edges of Y */
if (_HideInternalEdges(theGraph, IC->y) != OK)
return NOTOK;
/* Try to find a low X-Y path that excludes Y, then restore the
internal edges of Y. */
result = _MarkHighestXYPath(theGraph);
if (_RestoreInternalEdges(theGraph, stackBottom) != OK)
return NOTOK;
/* If we found the low X-Y path, then return. */
if (result == TRUE)
return OK;
/* Restore the original X-Y path and return with no error
(the search failure is reflected by no change to px and py */
if (_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
return OK;
}
/****************************************************************************
_TestForZtoWPath()
This function tests whether there is a path inside the bicomp leading from W
to some internal node of the x-y path. If there is, the path is marked (the
visited flags of its vertices and edges are set).
Upon function return, the marking (visited flag setting) of W distinguishes
whether the path was found.
The function returns NOTOK on internal error, OK otherwise.
Preconditions: All internal vertices have an obstruction type setting of
unknown, as do W and the bicomp root. There is an X-Y path marked visited.
So, we start a depth first search from W to find a visited vertex, except
we prune the search to ignore vertices whose obstruction type is other than
unknown. This ensures the path found, if any, avoids external face vertices,
including avoiding X and Y. Furthermore, the path search is completed without
traversing to R due to the obstructing X-Y path.
The depth first search has to "mark" the vertices it has seen as visited,
but the visited flags are already in use to distinguish the X-Y path.
So, we reuse the visitedInfo setting of each vertex. The core planarity
algorithm makes settings between 0 and N, so we will regard all of those
as indicating 'unvisited' by this method, and use -1 to indicate visited.
These markings need not be cleaned up because, if the desired path is found
the a K_{3,3} is isolated and if the desired path is not found then the
bicomp is reduced and the visitedInfo in the remaining vertices are set
appropriately for future Walkup processing of the core planarity algorithm.
For each vertex we visit, if it is an internal vertex on the X-Y path
(i.e. visited flag set and obstruction type unknown), then we want to stop
and unroll the stack to obtain the desired path (described below). If the
vertex is internal but not on the X-Y path (i.e. visited flag clear and
obstruction type unknown), then we want to visit its neighbors, except
those already marked visited by this method (i.e. those with visitedInfo
of -1) and those with a known obstruction type.
We want to manage the stack so that it when the desired vertex is found,
the stack contains the desired path. So, we do not simply push all the
neighbors of the vertex being visited. First, given that we have popped
some vertex-edge pair (v, e), we push *only* the next edge after e in
v's adjacency list (starting with the first if e is NIL) that leads to a
new 'eligible' vertex. An eligible vertex is one whose obstruction type
is unknown and whose visitedInfo is other than -1 (so, internal and not
yet processed by this method). Second, when we decide a new vertex w
adjacent to v is eligible, we push not only (v, e) but also (w, NIL).
When we later pop the vertex-edge pair containing NIL, we know that
the vertex obstruction type is unknown so we test whether its visited
flag is set (indicating an internal vertex on the X-Y path). If so, then
we can stop the depth first search, then use the vertices and edges
remaining on the stack to mark the desired path from the external face
vertex W to an internal vertex Z on the X-Y path.
If we pop (v, NIL) and find that the visited flag of v is clear, then it
is not the desired connection endpoint to the X-Y path. We need to process
all paths extending from it, but we don't want any of those paths to cycle
back to this vertex, so we mark it as ineligible by putting -1 in its
visitedInfo member. This is also the case in which the _first_ edge record e
leading from v to an eligible vertex w is obtained, whereupon we push both
(v, e) and (w, NIL). Eventually all paths leading from w to eligible
vertices will be explored, and if none find the desired vertex connection
to the X-Y path, then (v, e) is popped. Now we search the adjacency list of
v starting after e to find the _next_ edge record that indicates the an
eligible vertex to visit. None of the vertices processed while visiting paths
extending from w will be eligible anymore, so it can be seen that this method
is a depth first search. If no remaining edges from v indicate eligible
vertices, then nothing is pushed and we simply go to the next iteration,
which pops a 2-tuple containing the vertex u and the edge record e that
points to v. Finally, if the stack empties without finding the desired vertex,
then the first loop ends, and the second main loop does not mark a path because
the stack is empty.
****************************************************************************/
int _TestForZtoWPath(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
int v, e, w;
sp_ClearStack(theGraph->theStack);
sp_Push2(theGraph->theStack, IC->w, NIL);
while (!sp_IsEmpty(theGraph->theStack))
{
sp_Pop2(theGraph->theStack, v, e);
if (gp_IsNotArc(e))
{
// If the vertex is visited, then it is a member of the X-Y path
// Because it is being popped, its obstruction type is unknown because
// that is the only kind of vertex pushed.
// Hence, we break because we've found the desired path.
if (gp_GetVertexVisited(theGraph, v))
break;
// Mark this vertex as being visited by this method (i.e. ineligible
// to have processing started on it again)
gp_SetVertexVisitedInfo(theGraph, v, -1);
e = gp_GetFirstArc(theGraph, v);
}
else
e = gp_GetNextArc(theGraph, e);
// This while loop breaks on the first edge it finds that is eligible to be
// pushed. Once that happens, we break. The successive edges of a vertex are
// only pushed (see the else clause above) once all paths extending from v
// through e have been explored and found not to contain the desired path
while (gp_IsArc(e))
{
w = gp_GetNeighbor(theGraph, e);
// The test for w being a virtual vertex is just safeguarding the two subsequent calls,
// but it can never happen due to the obstructing X-Y path.
if (gp_IsNotVirtualVertex(theGraph, w) &&
gp_GetVertexVisitedInfo(theGraph, w) != -1 &&
gp_GetVertexObstructionType(theGraph, w) == VERTEX_OBSTRUCTIONTYPE_UNKNOWN)
{
sp_Push2(theGraph->theStack, v, e);
sp_Push2(theGraph->theStack, w, NIL);
break;
}
e = gp_GetNextArc(theGraph, e);
}
}
while (!sp_IsEmpty(theGraph->theStack))
{
sp_Pop2(theGraph->theStack, v, e);
gp_SetVertexVisited(theGraph, v);
gp_SetEdgeVisited(theGraph, e);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
}
return OK;
}
/****************************************************************************
_TestForStraddlingBridge()
We proceed on the path [V...u_{max}) from the current vertex V up to and
excluding u_{max}. For each vertex p, we test whether p has a least
ancestor less than u_{max} and whether p has a DFS child c that is not an
ancestor of X, Y and W and that has a connection to an ancestor of u_{max}
(in other words, whether the child C has a lowpoint less than u_{max}).
The sortedDFSChildLIst of the vertex p is scanned for the separated DFS
child c of least lowpoint, excluding the ancestor of X, Y and W.
If no bridge straddling u_{max} is found, the function returns NIL.
If a straddling bridge is found, the function returns a descendant d
of p in the subtree rooted by c such that d has a leastAncestor less
than u_{max}. Given the vertex d, the path through the straddling
bridge required in Minors E6 and E7 is easy to identify: Mark the
DFS tree path from d to p, and add and mark the edge from d to its
least ancestor.
OPTIMIZATION: If a straddling bridge is not found, then in each tree edge of
the path [V...u_{max}) we set the member noStraddle equal to u_{max}.
Then, we modify the above stated routine so that if it is testing
for a straddling bridge of u_{max} along this path, it will stop
if it encounters an edge with noStraddle equal to u_{max}.
Also, the optimization will only set noStraddle equal to
u_{max} on the portion of the path that is traversed. Finally, if
noStraddle is set to a value other than NIL, the setting will be
ignored and it will not be changed.
Due to this optimization, we do not traverse a path more than once
to find out whether a vertex on the path has a bridge that straddles
u_{max}. This leaves two questions:
1) What if a future step must determine whether there is a
straddling bridge of an ancestor of u_{max}?
2) What if a future step must determine whether there is a
straddling bridge of a descendant of u_{max}?
The condition described in the first question cannot occur because it
would imply the ability to detect a straddling bridge now.
The condition described by the second question may occur, but in the
future step, the bicomp now being tested for a K_{3,3} will be part of
a straddling bridge in that future step. Thus, the straddling
bridge query is asked at most twice along any DFS tree path.
****************************************************************************/
int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max)
{
isolatorContextP IC = &theGraph->IC;
int p, c, d, excludedChild, e;
p = IC->v;
excludedChild = gp_GetDFSChildFromRoot(theGraph, IC->r);
d = NIL;
// Starting at V, traverse the ancestor path to u_max looking for a straddling bridge
while (p > u_max)
{
// If we find a direct edge from p to an ancestor of u_max, the break.
if (gp_GetVertexLeastAncestor(theGraph, p) < u_max)
{
d = p;
break;
}
// Check for a path from p to an ancestor of u_max using the child of p
// with the least Lowpoint, except the child that is an ancestor of X, Y and W.
// It is possible to do this just using the sortedDFSChildList, but no point
// in not using the separatedDFSChildList
/*
{
int c = gp_GetVertexSortedDFSChildList(theGraph, p);
while (gp_IsVertex(c))
{
if (c != excludedChild && gp_IsSeparatedDFSChild(theGraph, c))
{
if (gp_GetVertexLowpoint(theGraph, c) < u_max)
break;
}
c = gp_GetVertexNextDFSChild(theGraph, p, c);
}
}
*/
c = context->VI[p].separatedDFSChildList;
if (c == excludedChild)
c = LCGetNext(context->separatedDFSChildLists, c, c);
if (gp_IsVertex(c) && gp_GetVertexLowpoint(theGraph, c) < u_max)
{
_FindUnembeddedEdgeToSubtree(theGraph, gp_GetVertexLowpoint(theGraph, c), c, &d);
break;
}
// Check for noStraddle of u_max, break if found
e = gp_GetFirstArc(theGraph, p);
if (context->E[e].noStraddle == u_max)
break;
// Go to the next ancestor
excludedChild = p;
p = gp_GetVertexParent(theGraph, p);
}
// If d is NIL, then no straddling bridge was found, so we do the noStraddle optimization.
if (gp_IsNotVertex(d))
{
c = IC->v;
while (c != p)
{
e = gp_GetFirstArc(theGraph, c);
if (gp_IsVertex(context->E[e].noStraddle))
break;
context->E[e].noStraddle = u_max;
c = gp_GetVertexParent(theGraph, c);
}
}
// Return either NIL indicating no bridge straddling u_max or the descendant d
// used to help mark a straddling bridge that was found by this test.
return d;
}
/****************************************************************************
_ReduceBicomp()
We want to reduce the given biconnected component to a 4-cycle plus an
internal edge connecting X and Y. Each edge is to be associated with a
path from the original graph, preserving the depth first search tree
paths that help connect the vertices R, X, Y, and W. If a K_{3,3} is later found,
the paths are restored, but it is necessary to preserve the DFS tree so that
functions like MarkDFSPath() will be able to pass through the restored bicomp.
Also, if a K_{3,3} is later found due to the merge blocker optimization, then the
internal X-Y path may be needed and, once the bicomp reduction is reversed,
a full DFS subtree connecting all vertices in the bicomp will need to be
restored or else functions that traverse the bicomp will not work.
For example, _FindK33WithMergeBlocker() invokes ChooseTypeOfNonplanarityMinor()
to help reconstruct the context under which the mergeBlocker was set.
ChooseTypeOfNonplanarityMinor() calls _ClearVisitedFlagsInBicomp(), which
depends on the DFS tree.
NOTE: The following are some general steps taken in this method:
1) All edges in the bicomp are marked unvisited
2) selected paths are marked visited
3) unvisited edges are deleted
4) the edges of the bicomp are marked unvisited again
5) the remaining paths of the bicomp are reduced
Some of the edges that get deleted in step 3 above may represent
paths that were reduced in prior embedder iterations. We delete
the reduction edge but not the path it represents.
If a K_{3,3} is ever found, then the edges of these reduced paths
are still in the graph, though not connected to anything important.
The desired K_{3,3} is marked visited, but step 4 above ensures that
these reduction paths are not marked visited. Hence, they will be
deleted when the K_{3,3} is isolated, and this routine does not
need to restore any reduced paths on the edges it deletes.
We also don't (and don't have the time to) restore any reduction
edges along the paths we intend to keep.
****************************************************************************/
int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R)
{
isolatorContextP IC = &theGraph->IC;
int min, mid, max, A, A_edge, B, B_edge;
int rxType, xwType, wyType, yrType, xyType;
/* The vertices in the bicomp need to be oriented so that functions
like MarkPathAlongBicompExtFace() will work. */
if (_OrientVerticesInBicomp(theGraph, R, 0) != OK)
return NOTOK;
/* The reduced edges start with a default type of 'tree' edge. The
tests below, which identify the additional non-tree paths
needed to complete the reduced bicomp, also identify which
reduced edges need to be cycle edges.*/
rxType = xwType = wyType = yrType = xyType = EDGE_TYPE_PARENT;
/* Now we calculate some values that help figure out the shape of the
DFS subtree whose structure will be retained in the bicomp. */
min = MIN3(IC->x, IC->y, IC->w);
max = MAX3(IC->x, IC->y, IC->w);
mid = MAX3(MIN(IC->x, IC->y), MIN(IC->x, IC->w), MIN(IC->y, IC->w));
/* If the order of descendendancy from V goes first to X, then it can
proceed either to W then Y or to Y then W */
if (min == IC->x)
{
/* A is a descendant adjacent to the current vertex by a cycle edge
whose DFS tree path to either mid or max is combined with the
cycle edge to form the path that will be reduced to the
external face cycle edge (V, max). */
A_edge = gp_GetLastArc(theGraph, IC->r);
A = gp_GetNeighbor(theGraph, A_edge);
yrType = EDGE_TYPE_BACK;
/* If Y is max, then a path parallel to the X-Y path will be a
second path reduced to a cycle edge. We find the neighbor B
of min=X on the X-Y path. The edge (B, min) is a cycle edge
that, along with the DFS tree path (B, ..., max), will be
retained and reduced to a cycle edge. */
if (max == IC->y)
{
B_edge = gp_GetLastArc(theGraph, IC->x);
while (B_edge != gp_GetFirstArc(theGraph, IC->x))
{
if (gp_GetEdgeVisited(theGraph, B_edge)) break;
B_edge = gp_GetPrevArc(theGraph, B_edge);
}
if (!gp_GetEdgeVisited(theGraph, B_edge))
return NOTOK;
B = gp_GetNeighbor(theGraph, B_edge);
xyType = EDGE_TYPE_BACK;
}
/* Otherwise, W is max so we find the neighbor B of min=X on the
lower external face path (X, ..., W), which excludes V. The
cycle edge (B, min) and the DFS tree path (B, max) will be
retained and reduced to a cycle edge.*/
else if (max == IC->w)
{
B_edge = gp_GetFirstArc(theGraph, IC->x);
B = gp_GetNeighbor(theGraph, B_edge);
xwType = EDGE_TYPE_BACK;
}
else return NOTOK;
}
/* Otherwise, the order of descendancy from V goes first to Y, then it
proceeds to either W then X or to X then W. The */
else
{
A_edge = gp_GetFirstArc(theGraph, IC->r);
A = gp_GetNeighbor(theGraph, A_edge);
rxType = EDGE_TYPE_BACK;
if (max == IC->x)
{
B_edge = gp_GetFirstArc(theGraph, IC->y);
while (B_edge != gp_GetLastArc(theGraph, IC->y))
{
if (gp_GetEdgeVisited(theGraph, B_edge)) break;
B_edge = gp_GetNextArc(theGraph, B_edge);
}
if (!gp_GetEdgeVisited(theGraph, B_edge))
return NOTOK;
B = gp_GetNeighbor(theGraph, B_edge);
xyType = EDGE_TYPE_BACK;
}
else if (max == IC->w)
{
B_edge = gp_GetLastArc(theGraph, IC->y);
B = gp_GetNeighbor(theGraph, B_edge);
wyType = EDGE_TYPE_BACK;
}
else return NOTOK;
}
/* Now that we have collected the information on which cycle edge and
which tree paths will actually be retained, we clear the visited
flags so the current X-Y path will not be retained (an X-Y path
formed mostly or entirely from DFS tree edges is retained). */
if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
/* Now we mark the tree path from the maximum numbered vertex up
to the bicomp root. This marks one of the following four paths:
Case 1. (V, ..., X=min, ..., W=mid, ..., Y=max)
Case 2. (V, ..., X=min, ..., Y=mid, ..., W=max)
Case 3. (V, ..., Y=min, ..., W=mid, ..., X=max)
Case 4. (V, ..., Y=min, ..., X=mid, ..., W=max) */
if (theGraph->functions.fpMarkDFSPath(theGraph, R, max) != OK)
return NOTOK;
/* Now we use A to mark a path on the external face corresponding to:
Case 1. (V, ..., Y=max)
Case 2. (V, ..., Y=mid)
Case 3. (V, ..., X=max)
Case 4. (V, ..., X=mid) */
if (theGraph->functions.fpMarkDFSPath(theGraph, min==IC->x ? IC->y : IC->x, A) != OK)
return NOTOK;
gp_SetEdgeVisited(theGraph, A_edge);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, A_edge));
/* Now we use B to mark either an X-Y path or a path of the external face
corresponding to:
Case 1. (X=min, ..., B, ..., Y=max)
Case 2. (X=min, ..., B, ..., W=max)
Case 3. (Y=min, ..., B, ..., X=max)
Case 4. (Y=min, ..., B, ..., W=max) */
if (theGraph->functions.fpMarkDFSPath(theGraph, max, B) != OK)
return NOTOK;
gp_SetEdgeVisited(theGraph, B_edge);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, B_edge));
/* Delete the unmarked edges in the bicomp. Note that if an unmarked edge
* represents a reduced path, then only the reduction edge is deleted here.
* The path it represents is only deleted later (see NOTE above) */
if (_K33Search_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK)
return NOTOK;
/* Clear all visited flags in the bicomp.
This is the important "step 4" mentioned in the NOTE above */
if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
/* Clear all orientation signs in the bicomp.
Note that the whole bicomp may not be properly oriented at this point
because we may have exchanged external face paths for internal
DFS tree paths. However, the reduced bicomp will be properly
oriented, and the paths of degree 2 vertices will have their
orientations fixed if/when reduction edges are restored. */
if (_ClearInvertedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
/* Reduce the paths to single edges.
Note that although the whole bicomp may not be properly oriented at this
point (as noted above), the four principal vertices R, X, W and Y still
are consistently oriented with one another, e.g. R's link[0] indicates
the external face path toward X that excludes W and Y, and X's link[1]
indicates that same path. */
if (_ReduceExternalFacePathToEdge(theGraph, context, R, IC->x, rxType) != OK ||
_ReduceExternalFacePathToEdge(theGraph, context, IC->x, IC->w, xwType) != OK ||
_ReduceExternalFacePathToEdge(theGraph, context, IC->w, IC->y, wyType) != OK ||
_ReduceExternalFacePathToEdge(theGraph, context, IC->y, R, yrType) != OK)
return NOTOK;
if (_ReduceXYPathToEdge(theGraph, context, IC->x, IC->y, xyType) != OK)
return NOTOK;
return OK;
}
/********************************************************************
Edge deletion that occurs during a reduction or restoration of a
reduction is augmented by clearing the K_{3,3} search-specific
data members. This is augmentation is not needed in the delete edge
operations that happen once a K_{3,3} homeomorph has been found and
marked for isolation.
********************************************************************/
int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink)
{
_K33Search_InitEdgeRec(context, e);
_K33Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e));
return gp_DeleteEdge(theGraph, e, nextLink);
}
/********************************************************************
_K33Search_DeleteUnmarkedEdgesInBicomp()
This function deletes from a given biconnected component all edges
whose visited member is zero.
The stack is used but preserved. In debug mode, NOTOK can result if
there is a stack overflow. This method pushes at most one integer
per vertex in the bicomp.
This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls
the overloaded _K33_DeleteEdge() rather than gp_DeleteEdge()
Returns OK on success, NOTOK on implementation failure
********************************************************************/
int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot)
{
int V, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
e = gp_GetFirstArc(theGraph, V);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetEdgeVisited(theGraph, e)
? gp_GetNextArc(theGraph, e)
: _K33Search_DeleteEdge(theGraph, context, e, 0);
}
}
return OK;
}
/****************************************************************************
_ReduceExternalFacePathToEdge()
****************************************************************************/
int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType)
{
int prevLink, v, w, e;
/* If the path is a single edge, then no need for a reduction */
prevLink = 1;
v = _GetNeighborOnExtFace(theGraph, u, &prevLink);
if (v == x)
{
gp_SetExtFaceVertex(theGraph, u, 0, x);
gp_SetExtFaceVertex(theGraph, x, 1, u);
return OK;
}
/* We have the endpoints u and x of the path, and we just computed the
first vertex internal to the path and a neighbor of u. Now we
compute the vertex internal to the path and a neighbor of x. */
prevLink = 0;
w = _GetNeighborOnExtFace(theGraph, x, &prevLink);
/* Delete the two edges that connect the path to the bicomp.
If either edge is a reduction edge, then we have to restore
the path it represents. We can only afford to visit the
endpoints of the path.
Note that in the restored path, the edge incident to each
endpoint of the original path is a newly added edge,
not a reduction edge. */
e = gp_GetFirstArc(theGraph, u);
if (gp_IsVertex(context->E[e].pathConnector))
{
if (_RestoreReducedPath(theGraph, context, e) != OK)
return NOTOK;
e = gp_GetFirstArc(theGraph, u);
v = gp_GetNeighbor(theGraph, e);
}
_K33Search_DeleteEdge(theGraph, context, e, 0);
e = gp_GetLastArc(theGraph, x);
if (gp_IsVertex(context->E[e].pathConnector))
{
if (_RestoreReducedPath(theGraph, context, e) != OK)
return NOTOK;
e = gp_GetLastArc(theGraph, x);
w = gp_GetNeighbor(theGraph, e);
}
_K33Search_DeleteEdge(theGraph, context, e, 0);
/* Add the reduction edge, then set its path connectors so the original
path can be recovered and set the edge type so the essential structure
of the DFS tree can be maintained (The 'Do X to Bicomp' functions
and functions like MarkDFSPath(0 depend on this). */
gp_AddEdge(theGraph, u, 0, x, 1);
e = gp_GetFirstArc(theGraph, u);
context->E[e].pathConnector = v;
gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType));
e = gp_GetLastArc(theGraph, x);
context->E[e].pathConnector = w;
gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType));
/* Set the external face info */
gp_SetExtFaceVertex(theGraph, u, 0, x);
gp_SetExtFaceVertex(theGraph, x, 1, u);
return OK;
}
/****************************************************************************
_ReduceXYPathToEdge()
****************************************************************************/
int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType)
{
int e, v, w;
e = gp_GetFirstArc(theGraph, u);
e = gp_GetNextArc(theGraph, e);
v = gp_GetNeighbor(theGraph, e);
/* If the XY-path is a single edge, then no reduction is needed */
if (v == x)
return OK;
/* Otherwise, remove the two edges that join the XY-path to the bicomp */
if (gp_IsVertex(context->E[e].pathConnector))
{
if (_RestoreReducedPath(theGraph, context, e) != OK)
return NOTOK;
e = gp_GetFirstArc(theGraph, u);
e = gp_GetNextArc(theGraph, e);
v = gp_GetNeighbor(theGraph, e);
}
_K33Search_DeleteEdge(theGraph, context, e, 0);
e = gp_GetFirstArc(theGraph, x);
e = gp_GetNextArc(theGraph, e);
w = gp_GetNeighbor(theGraph, e);
if (gp_IsVertex(context->E[e].pathConnector))
{
if (_RestoreReducedPath(theGraph, context, e) != OK)
return NOTOK;
e = gp_GetFirstArc(theGraph, x);
e = gp_GetNextArc(theGraph, e);
w = gp_GetNeighbor(theGraph, e);
}
_K33Search_DeleteEdge(theGraph, context, e, 0);
/* Now add a single edge to represent the XY-path */
gp_InsertEdge(theGraph, u, gp_GetFirstArc(theGraph, u), 0,
x, gp_GetFirstArc(theGraph, x), 0);
/* Now set up the path connectors so the original XY-path can be recovered if needed.
Also, set the reduction edge's type to preserve the DFS tree structure */
e = gp_GetFirstArc(theGraph, u);
e = gp_GetNextArc(theGraph, e);
context->E[e].pathConnector = v;
gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType));
e = gp_GetFirstArc(theGraph, x);
e = gp_GetNextArc(theGraph, e);
context->E[e].pathConnector = w;
gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType));
return OK;
}
/****************************************************************************
_RestoreReducedPath()
Given an edge record of an edge used to reduce a path, we want to restore
the path in constant time.
The path may contain more reduction edges internally, but we do not
search for and process those since it would violate the constant time
bound required of this function.
return OK on success, NOTOK on failure
****************************************************************************/
int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e)
{
int eTwin, u, v, w, x;
int e0, e1, eTwin0, eTwin1;
if (gp_IsNotVertex(context->E[e].pathConnector))
return OK;
eTwin = gp_GetTwinArc(theGraph, e);
u = gp_GetNeighbor(theGraph, eTwin);
v = context->E[e].pathConnector;
w = context->E[eTwin].pathConnector;
x = gp_GetNeighbor(theGraph, e);
/* Get the locations of the edge records between which the new
edge records must be added in order to reconnect the path
parallel to the edge. */
e0 = gp_GetNextArc(theGraph, e);
e1 = gp_GetPrevArc(theGraph, e);
eTwin0 = gp_GetNextArc(theGraph, eTwin);
eTwin1 = gp_GetPrevArc(theGraph, eTwin);
/* We first delete the edge represented by e and eTwin. We do so before
restoring the path to ensure we do not exceed the maximum arc capacity. */
_K33Search_DeleteEdge(theGraph, context, e, 0);
/* Now we add the two edges to reconnect the reduced path represented
by the edge [e, eTwin]. The edge record in u is added between e0 and e1.
Likewise, the new edge record in x is added between eTwin0 and eTwin1. */
if (gp_IsArc(e0))
{
if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK)
return NOTOK;
}
else
{
if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK)
return NOTOK;
}
if (gp_IsArc(eTwin0))
{
if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK)
return NOTOK;
}
else
{
if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK)
return NOTOK;
}
// Set the types of the newly added edges. In both cases, the first of the two
// vertex parameters is known to be degree 2 because they are internal to the
// path being restored, so this operation is constant time.
if (_SetEdgeType(theGraph, v, u) != OK ||
_SetEdgeType(theGraph, w, x) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_RestoreAndOrientReducedPaths()
This function searches the embedding for any edges that are specially marked
as being representative of a path that was previously reduced to a
single edge by _ReduceBicomp(). The edge is replaced by the path.
Note that the new path may contain more reduction edges, and these will be
iteratively expanded by the outer for loop.
If the edge records of an edge being expanded are the first or last arcs
of the edge's vertex endpoints, then the edge may be along the external face.
If so, then the vertices along the path being restored must be given a
consistent orientation with the endpoints. It is expected that the embedding
will have been oriented prior to this operation.
****************************************************************************/
int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context)
{
int EsizeOccupied, e, eTwin, u, v, w, x, visited;
int e0, eTwin0, e1, eTwin1;
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;)
{
if (gp_IsVertex(context->E[e].pathConnector))
{
visited = gp_GetEdgeVisited(theGraph, e);
eTwin = gp_GetTwinArc(theGraph, e);
u = gp_GetNeighbor(theGraph, eTwin);
v = context->E[e].pathConnector;
w = context->E[eTwin].pathConnector;
x = gp_GetNeighbor(theGraph, e);
/* Now we need the predecessor and successor edge records
of e and eTwin. The edge (u, v) will be inserted so
that the record in u's adjacency list that indicates v
will be between e0 and e1. Likewise, the edge record
(x -> w) will be placed between eTwin0 and eTwin1. */
e0 = gp_GetNextArc(theGraph, e);
e1 = gp_GetPrevArc(theGraph, e);
eTwin0 = gp_GetNextArc(theGraph, eTwin);
eTwin1 = gp_GetPrevArc(theGraph, eTwin);
/* We first delete the edge represented by e and eTwin. We do so before
restoring the path to ensure we do not exceed the maximum arc capacity. */
_K33Search_DeleteEdge(theGraph, context, e, 0);
/* Now we add the two edges to reconnect the reduced path represented
by the edge [e, eTwin]. The edge record in u is added between e0 and e1.
Likewise, the new edge record in x is added between eTwin0 and eTwin1. */
if (gp_IsArc(e0))
{
if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK)
return NOTOK;
}
else
{
if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK)
return NOTOK;
}
if (gp_IsArc(eTwin0))
{
if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK)
return NOTOK;
}
else
{
if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK)
return NOTOK;
}
/* Set the types of the newly added edges */
if (_SetEdgeType(theGraph, u, v) != OK ||
_SetEdgeType(theGraph, w, x) != OK)
return NOTOK;
/* We determine whether the reduction edge may be on the external face,
in which case we will need to ensure that the vertices on the path
being restored are consistently oriented. This will accommodate
future invocations of MarkPathAlongBicompExtFace().
Note: If e0, e1, eTwin0 or eTwin1 is not an edge, then it is
because we've walked off the end of the edge record list,
which happens when e and eTwin are either the first or
last edge of the containing vertex. In turn, the first
and last edges of a vertex are the ones that hold it onto
the external face, if it is on the external face. */
if ((gp_IsNotArc(e0) && gp_IsNotArc(eTwin1)) || (gp_IsNotArc(e1) && gp_IsNotArc(eTwin0)))
{
if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK)
return NOTOK;
}
/* The internal XY path was already marked as part of the decision logic
that made us decide we could find a K_{3,3} and hence that we should
reverse all of the reductions. Subsequent code counts on the fact
that the X-Y path is already marked, so if we replace a marked edge
with a path, then we need to mark the path. Similarly, for an unmarked
edge, the replacement path should be unmarked. */
if (visited)
{
if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK)
return NOTOK;
}
else
{
if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK)
return NOTOK;
}
}
else e+=2;
}
return OK;
}
/****************************************************************************
_MarkStraddlingBridgePath()
****************************************************************************/
int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d)
{
isolatorContextP IC = &theGraph->IC;
int p, e;
/* Find the point of intersection p between the path (v ... u_max)
and the path (d ... u_max). */
if (theGraph->functions.fpMarkDFSPath(theGraph, u_max, IC->r) != OK)
return NOTOK;
p = d;
while (!gp_GetVertexVisited(theGraph, p))
{
gp_SetVertexVisited(theGraph, p);
e = gp_GetFirstArc(theGraph, p);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
break;
e = gp_GetNextArc(theGraph, e);
}
gp_SetEdgeVisited(theGraph, e);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
p = gp_GetNeighbor(theGraph, e);
/* If p is a root copy, mark it visited and skip to the parent copy */
if (gp_IsVirtualVertex(theGraph, p))
{
gp_SetVertexVisited(theGraph, p);
p = gp_GetPrimaryVertexFromRoot(theGraph, p);
}
}
/* Unmark the path (p ... u_max), which was marked to help find p.
The path from v to u_{max} is not needed to form a K_{3,3} except
for the portion of the path up to p that, with the straddling
bridge path, comprises part of the connection to u_d. In the
minor, the path between v and p is edge contracted. */
while (p != u_max)
{
e = gp_GetFirstArc(theGraph, p);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
break;
e = gp_GetNextArc(theGraph, e);
}
gp_ClearEdgeVisited(theGraph, e);
gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
p = gp_GetNeighbor(theGraph, e);
gp_ClearVertexVisited(theGraph, p);
/* If p is a root copy, clear its visited flag and skip to the
parent copy */
if (gp_IsVirtualVertex(theGraph, p))
{
p = gp_GetPrimaryVertexFromRoot(theGraph, p);
gp_ClearVertexVisited(theGraph, p);
}
}
/* The straddling bridge must join the path (u_max ... u_min). If u_d is an
ancestor of u_min, then mark the path that joins u_d to u_min. */
if (u_d < u_min)
if (theGraph->functions.fpMarkDFSPath(theGraph, u_d, u_min) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorE5()
The paths (x, w), (y, w) and (v, u_{max}) are not needed.
The x-y path and the internal w-z path are already marked.
****************************************************************************/
int _IsolateMinorE5(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux,IC->uy,IC->uz),
MAX3(IC->ux,IC->uy,IC->uz)) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorE6()
The paths (x, y), (v, w) and (v, u_{max}) are not needed.
The path through the straddling bridge that connects from an ancestor of
u_{max} to v is required, but it may connnect to an ancestor p of v.
In such a case, the path (v, p) is required, while (p, u_{max}) is not.
****************************************************************************/
int _IsolateMinorE6(graphP theGraph, K33SearchContext *context)
{
isolatorContextP IC = &theGraph->IC;
int u_min, u_max, d, u_d;
/* Clear the previously marked x-y path */
if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK)
return NOTOK;
/* Clear dw to stop the marking of path (v, w) */
IC->dw = NIL;
/* Mark (v, ..., x, ..., w, ..., y, ... v) */
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK)
return NOTOK;
/* Mark the path through the straddling bridge (except for the final
edge (u_d, d) which is added last by convention). */
u_min = MIN3(IC->ux,IC->uy,IC->uz);
u_max = MAX3(IC->ux,IC->uy,IC->uz);
d = _TestForStraddlingBridge(theGraph, context, u_max);
u_d = gp_GetVertexLeastAncestor(theGraph, d);
if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK)
return NOTOK;
/* Make the final markings and edge additions */
if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK ||
_AddAndMarkEdge(theGraph, u_d, d) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateMinorE7()
****************************************************************************/
int _IsolateMinorE7(graphP theGraph, K33SearchContext *context)
{
isolatorContextP IC = &theGraph->IC;
int u_min, u_max, d, u_d;
/* Mark the appropriate two portions of the external face depending on
symmetry condition */
if (IC->uy < IC->ux)
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK)
return NOTOK;
}
else
{
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK ||
_MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK)
return NOTOK;
}
/* Mark the path through the straddling bridge (except for the final
edge (u_d, d) which is added last by convention). */
u_min = MIN3(IC->ux,IC->uy,IC->uz);
u_max = MAX3(IC->ux,IC->uy,IC->uz);
d = _TestForStraddlingBridge(theGraph, context, u_max);
u_d = gp_GetVertexLeastAncestor(theGraph, d);
if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK)
return NOTOK;
/* Make the final markings and edge additions */
if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK ||
_MarkDFSPathsToDescendants(theGraph) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkUnembeddedEdges(theGraph) != OK ||
_AddAndMarkEdge(theGraph, u_d, d) != OK)
return NOTOK;
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK33Search.h 0000664 0000000 0000000 00000000650 12716155161 0024320 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_K33SEARCH_H
#define GRAPH_K33SEARCH_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphStructures.h"
#ifdef __cplusplus
extern "C" {
#endif
#define K33SEARCH_NAME "K33Search"
int gp_AttachK33Search(graphP theGraph);
int gp_DetachK33Search(graphP theGraph);
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK33Search.private.h 0000664 0000000 0000000 00000002513 12716155161 0025771 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_K33SEARCH_PRIVATE_H
#define GRAPH_K33SEARCH_PRIVATE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graph.h"
#ifdef __cplusplus
extern "C" {
#endif
// Additional equipment for each EdgeRec
typedef struct
{
int noStraddle, pathConnector;
} K33Search_EdgeRec;
typedef K33Search_EdgeRec * K33Search_EdgeRecP;
// Additional equipment for each primary vertex
typedef struct
{
int separatedDFSChildList, backArcList, mergeBlocker;
} K33Search_VertexInfo;
typedef K33Search_VertexInfo * K33Search_VertexInfoP;
typedef struct
{
// Helps distinguish initialize from re-initialize
int initialized;
// The graph that this context augments
graphP theGraph;
// Parallel array for additional edge level equipment
K33Search_EdgeRecP E;
// Parallel array for additional vertex info level equipment
K33Search_VertexInfoP VI;
// Storage for the separatedDFSChildLists, and
// to help with linear time sorting of same by lowpoints
listCollectionP separatedDFSChildLists;
int *buckets;
listCollectionP bin;
// Overloaded function pointers
graphFunctionTable functions;
} K33SearchContext;
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK33Search_Extensions.c 0000664 0000000 0000000 00000065601 12716155161 0026541 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "graphK33Search.private.h"
#include "graphK33Search.h"
extern int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker);
extern int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker);
extern int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R);
extern int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts);
extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,
int *imageVerts, int maxNumImageVerts);
extern int _TestSubgraph(graphP theSubgraph, graphP theGraph);
/* Forward declarations of local functions */
void _K33Search_ClearStructures(K33SearchContext *context);
int _K33Search_CreateStructures(K33SearchContext *context);
int _K33Search_InitStructures(K33SearchContext *context);
void _K33Search_InitEdgeRec(K33SearchContext *context, int e);
void _K33Search_InitVertexInfo(K33SearchContext *context, int v);
/* Forward declarations of overloading functions */
int _K33Search_EmbeddingInitialize(graphP theGraph);
void _CreateBackArcLists(graphP theGraph, K33SearchContext *context);
void _CreateSeparatedDFSChildLists(graphP theGraph, K33SearchContext *context);
void _K33Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink);
void _K33Search_MergeVertex(graphP theGraph, int W, int WPrevLink, int R);
int _K33Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R);
int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
int _K33Search_InitGraph(graphP theGraph, int N);
void _K33Search_ReinitializeGraph(graphP theGraph);
int _K33Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);
/* Forward declarations of functions used by the extension system */
void *_K33Search_DupContext(void *pContext, void *theGraph);
void _K33Search_FreeContext(void *);
/****************************************************************************
* K33SEARCH_ID - the variable used to hold the integer identifier for this
* extension, enabling this feature's extension context to be distinguished
* from other features' extension contexts that may be attached to a graph.
****************************************************************************/
int K33SEARCH_ID = 0;
/****************************************************************************
gp_AttachK33Search()
This function adjusts the graph data structure to attach the K3,3 search
feature.
****************************************************************************/
int gp_AttachK33Search(graphP theGraph)
{
K33SearchContext *context = NULL;
// If the K3,3 search feature has already been attached to the graph,
// then there is no need to attach it again
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
return OK;
}
// Allocate a new extension context
context = (K33SearchContext *) malloc(sizeof(K33SearchContext));
if (context == NULL)
{
return NOTOK;
}
// First, tell the context that it is not initialized
context->initialized = 0;
// Save a pointer to theGraph in the context
context->theGraph = theGraph;
// Put the overload functions into the context function table.
// gp_AddExtension will overload the graph's functions with these, and
// return the base function pointers in the context function table
memset(&context->functions, 0, sizeof(graphFunctionTable));
context->functions.fpEmbeddingInitialize = _K33Search_EmbeddingInitialize;
context->functions.fpEmbedBackEdgeToDescendant = _K33Search_EmbedBackEdgeToDescendant;
context->functions.fpMergeBicomps = _K33Search_MergeBicomps;
context->functions.fpMergeVertex = _K33Search_MergeVertex;
context->functions.fpHandleBlockedBicomp = _K33Search_HandleBlockedBicomp;
context->functions.fpEmbedPostprocess = _K33Search_EmbedPostprocess;
context->functions.fpCheckEmbeddingIntegrity = _K33Search_CheckEmbeddingIntegrity;
context->functions.fpCheckObstructionIntegrity = _K33Search_CheckObstructionIntegrity;
context->functions.fpInitGraph = _K33Search_InitGraph;
context->functions.fpReinitializeGraph = _K33Search_ReinitializeGraph;
context->functions.fpEnsureArcCapacity = _K33Search_EnsureArcCapacity;
_K33Search_ClearStructures(context);
// Store the K33 search context, including the data structure and the
// function pointers, as an extension of the graph
if (gp_AddExtension(theGraph, &K33SEARCH_ID, (void *) context,
_K33Search_DupContext, _K33Search_FreeContext,
&context->functions) != OK)
{
_K33Search_FreeContext(context);
return NOTOK;
}
// Create the K33-specific structures if the size of the graph is known
// Attach functions are always invoked after gp_New(), but if a graph
// extension must be attached before gp_Read(), then the attachment
// also happens before gp_InitGraph(), which means N==0.
// However, sometimes a feature is attached after gp_InitGraph(), in
// which case N > 0
if (theGraph->N > 0)
{
if (_K33Search_CreateStructures(context) != OK ||
_K33Search_InitStructures(context) != OK)
{
_K33Search_FreeContext(context);
return NOTOK;
}
}
return OK;
}
/********************************************************************
gp_DetachK33Search()
********************************************************************/
int gp_DetachK33Search(graphP theGraph)
{
return gp_RemoveExtension(theGraph, K33SEARCH_ID);
}
/********************************************************************
_K33Search_ClearStructures()
********************************************************************/
void _K33Search_ClearStructures(K33SearchContext *context)
{
if (!context->initialized)
{
// Before initialization, the pointers are stray, not NULL
// Once NULL or allocated, free() or LCFree() can do the job
context->E = NULL;
context->VI = NULL;
context->separatedDFSChildLists = NULL;
context->buckets = NULL;
context->bin = NULL;
context->initialized = 1;
}
else
{
if (context->E != NULL)
{
free(context->E);
context->E = NULL;
}
if (context->VI != NULL)
{
free(context->VI);
context->VI = NULL;
}
LCFree(&context->separatedDFSChildLists);
if (context->buckets != NULL)
{
free(context->buckets);
context->buckets = NULL;
}
LCFree(&context->bin);
}
}
/********************************************************************
_K33Search_CreateStructures()
Create uninitialized structures for the vertex and edge
levels, and initialized structures for the graph level
********************************************************************/
int _K33Search_CreateStructures(K33SearchContext *context)
{
int VIsize = gp_PrimaryVertexIndexBound(context->theGraph);
int Esize = gp_EdgeIndexBound(context->theGraph);
if (context->theGraph->N <= 0)
return NOTOK;
if ((context->E = (K33Search_EdgeRecP) malloc(Esize*sizeof(K33Search_EdgeRec))) == NULL ||
(context->VI = (K33Search_VertexInfoP) malloc(VIsize*sizeof(K33Search_VertexInfo))) == NULL ||
(context->separatedDFSChildLists = LCNew(VIsize)) == NULL ||
(context->buckets = (int *) malloc(VIsize * sizeof(int))) == NULL ||
(context->bin = LCNew(VIsize)) == NULL
)
{
return NOTOK;
}
return OK;
}
/********************************************************************
_K33Search_InitStructures()
********************************************************************/
int _K33Search_InitStructures(K33SearchContext *context)
{
#if NIL == 0 || NIL == -1
memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(K33Search_VertexInfo));
memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K33Search_EdgeRec));
#else
graphP theGraph = context->theGraph;
int v, e, Esize;
if (theGraph->N <= 0)
return OK;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
_K33Search_InitVertexInfo(context, v);
Esize = gp_EdgeIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < Esize; e++)
_K33Search_InitEdgeRec(context, e);
#endif
return OK;
}
/********************************************************************
********************************************************************/
int _K33Search_InitGraph(graphP theGraph, int N)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context == NULL)
return NOTOK;
theGraph->N = N;
theGraph->NV = N;
if (theGraph->arcCapacity == 0)
theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N;
if (_K33Search_CreateStructures(context) != OK ||
_K33Search_InitStructures(context) != OK)
return NOTOK;
context->functions.fpInitGraph(theGraph, N);
return OK;
}
/********************************************************************
********************************************************************/
void _K33Search_ReinitializeGraph(graphP theGraph)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
// Reinitialize the graph
context->functions.fpReinitializeGraph(theGraph);
// Do the reinitialization that is specific to this module
_K33Search_InitStructures(context);
LCReset(context->separatedDFSChildLists);
LCReset(context->bin);
}
}
/********************************************************************
The current implementation does not support an increase of arc
(edge record) capacity once the extension is attached to the graph
data structure. This is only due to not being necessary to support.
For now, it is easy to ensure the correct capacity before attaching
the extension, but support could be added later if there is some
reason to do so.
********************************************************************/
int _K33Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)
{
return NOTOK;
}
/********************************************************************
_K33Search_DupContext()
********************************************************************/
void *_K33Search_DupContext(void *pContext, void *theGraph)
{
K33SearchContext *context = (K33SearchContext *) pContext;
K33SearchContext *newContext = (K33SearchContext *) malloc(sizeof(K33SearchContext));
if (newContext != NULL)
{
int VIsize = gp_PrimaryVertexIndexBound((graphP) theGraph);
int Esize = gp_EdgeIndexBound((graphP) theGraph);
*newContext = *context;
newContext->theGraph = (graphP) theGraph;
newContext->initialized = 0;
_K33Search_ClearStructures(newContext);
if (((graphP) theGraph)->N > 0)
{
if (_K33Search_CreateStructures(newContext) != OK)
{
_K33Search_FreeContext(newContext);
return NULL;
}
memcpy(newContext->E, context->E, Esize*sizeof(K33Search_EdgeRec));
memcpy(newContext->VI, context->VI, VIsize*sizeof(K33Search_VertexInfo));
LCCopy(newContext->separatedDFSChildLists, context->separatedDFSChildLists);
}
}
return newContext;
}
/********************************************************************
_K33Search_FreeContext()
********************************************************************/
void _K33Search_FreeContext(void *pContext)
{
K33SearchContext *context = (K33SearchContext *) pContext;
_K33Search_ClearStructures(context);
free(pContext);
}
/********************************************************************
_K33Search_EmbeddingInitialize()
This method overloads the embedding initialization phase of the
core planarity algorithm to provide post-processing that creates
the back arcs list and separated DFS child list (sorted by
lowpoint) for each vertex.
********************************************************************/
int _K33Search_EmbeddingInitialize(graphP theGraph)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
if (context->functions.fpEmbeddingInitialize(theGraph) != OK)
return NOTOK;
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
_CreateBackArcLists(theGraph, context);
_CreateSeparatedDFSChildLists(theGraph, context);
}
return OK;
}
return NOTOK;
}
/********************************************************************
_CreateBackArcLists()
********************************************************************/
void _CreateBackArcLists(graphP theGraph, K33SearchContext *context)
{
int v, e, eTwin, ancestor;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
e = gp_GetVertexFwdArcList(theGraph, v);
while (gp_IsArc(e))
{
// Get the ancestor endpoint and the associated back arc
ancestor = gp_GetNeighbor(theGraph, e);
eTwin = gp_GetTwinArc(theGraph, e);
// Put it into the back arc list of the ancestor
if (gp_IsNotArc(context->VI[ancestor].backArcList))
{
context->VI[ancestor].backArcList = eTwin;
gp_SetPrevArc(theGraph, eTwin, eTwin);
gp_SetNextArc(theGraph, eTwin, eTwin);
}
else
{
int eHead = context->VI[ancestor].backArcList;
int eTail = gp_GetPrevArc(theGraph, eHead);
gp_SetPrevArc(theGraph, eTwin, eTail);
gp_SetNextArc(theGraph, eTwin, eHead);
gp_SetPrevArc(theGraph, eHead, eTwin);
gp_SetNextArc(theGraph, eTail, eTwin);
}
// Advance to the next forward edge
e = gp_GetNextArc(theGraph, e);
if (e == gp_GetVertexFwdArcList(theGraph, v))
e = NIL;
}
}
}
/********************************************************************
_CreateSeparatedDFSChildLists()
Each vertex gets a list of its DFS children, sorted by lowpoint.
********************************************************************/
void _CreateSeparatedDFSChildLists(graphP theGraph, K33SearchContext *context)
{
int *buckets;
listCollectionP bin;
int v, L, DFSParent, theList;
buckets = context->buckets;
bin = context->bin;
// Initialize the bin and all the buckets to be empty
LCReset(bin);
for (L = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, L); L++)
buckets[L] = NIL;
// For each vertex, add it to the bucket whose index is equal to the lowpoint of the vertex.
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
L = gp_GetVertexLowpoint(theGraph, v);
buckets[L] = LCAppend(bin, buckets[L], v);
}
// For each bucket, add each vertex in the bucket to the separatedDFSChildList of its DFSParent.
// Since lower numbered buckets are processed before higher numbered buckets, vertices with lower
// lowpoint values are added before those with higher lowpoint values, so the separatedDFSChildList
// of each vertex is sorted by lowpoint
for (L = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, L); L++)
{
v = buckets[L];
// Loop through all the vertices with lowpoint L, putting each in the list of its parent
while (gp_IsVertex(v))
{
DFSParent = gp_GetVertexParent(theGraph, v);
if (gp_IsVertex(DFSParent) && DFSParent != v)
{
theList = context->VI[DFSParent].separatedDFSChildList;
theList = LCAppend(context->separatedDFSChildLists, theList, v);
context->VI[DFSParent].separatedDFSChildList = theList;
}
v = LCGetNext(bin, buckets[L], v);
}
}
}
/********************************************************************
_K33Search_EmbedBackEdgeToDescendant()
The forward and back arcs of the cycle edge are embedded by the planarity
version of this function.
However, for K_{3,3} subgraph homeomorphism, we also maintain the
list of unembedded back arcs, so we need to remove the back arc from
that list since it is now being put back into the adjacency list.
********************************************************************/
void _K33Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
// K33 search may have been attached, but not enabled
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
// Get the fwdArc from the adjacentTo field, and use it to get the backArc
int backArc = gp_GetTwinArc(theGraph, gp_GetVertexPertinentEdge(theGraph, W));
// Remove the backArc from the backArcList
if (context->VI[W].backArcList == backArc)
{
if (gp_GetNextArc(theGraph, backArc) == backArc)
context->VI[W].backArcList = NIL;
else context->VI[W].backArcList = gp_GetNextArc(theGraph, backArc);
}
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, backArc), gp_GetNextArc(theGraph, backArc));
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, backArc), gp_GetPrevArc(theGraph, backArc));
}
// Invoke the superclass version of the function
context->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink);
}
}
/********************************************************************
This override of _MergeBicomps() detects a special merge block
that indicates a K3,3 can be found. The merge blocker is an
optimization needed for one case for which detecting a K3,3
could not be done in linear time by direct searching of a
path of ancestors that is naturally explored eventually by
the core planarity algorithm.
Returns OK for a successful merge, NOTOK on an internal failure,
or NONEMBEDDABLE if the merge was blocked, in which case
a K_{3,3} homeomorph was isolated.
********************************************************************/
int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
/* If the merge is blocked, then a K_{3,3} homeomorph is isolated,
and NONEMBEDDABLE is returned so that the Walkdown terminates */
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
int mergeBlocker;
// We want to test all merge points on the stack
// as well as W, since the connection will go
// from W. So we push W as a 'degenerate' merge point.
sp_Push2(theGraph->theStack, W, WPrevLink);
sp_Push2(theGraph->theStack, NIL, NIL);
if (_SearchForMergeBlocker(theGraph, context, v, &mergeBlocker) != OK)
return NOTOK;
if (gp_IsVertex(mergeBlocker))
{
if (_FindK33WithMergeBlocker(theGraph, context, v, mergeBlocker) != OK)
return NOTOK;
return NONEMBEDDABLE;
}
// If no merge blocker was found, then remove W from the stack.
sp_Pop2(theGraph->theStack, W, WPrevLink);
sp_Pop2(theGraph->theStack, W, WPrevLink);
}
// If the merge was not blocked, then we perform the merge
// When not doing a K3,3 search, then the merge is not
// blocked as far as the K3,3 search method is concerned
// Another algorithms could overload MergeBicomps and block
// merges under certain conditions, but those would be based
// on data maintained by the extension that implements the
// other algorithm-- if *that* algorithm is the one being run
return context->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink);
}
return NOTOK;
}
/********************************************************************
_K33Search_MergeVertex()
Overload of merge vertex that does basic behavior but also removes
the DFS child associated with R from the separatedDFSChildList of W.
********************************************************************/
void _K33Search_MergeVertex(graphP theGraph, int W, int WPrevLink, int R)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
int theList = context->VI[W].separatedDFSChildList;
theList = LCDelete(context->separatedDFSChildLists, theList, gp_GetDFSChildFromRoot(theGraph, R));
context->VI[W].separatedDFSChildList = theList;
}
context->functions.fpMergeVertex(theGraph, W, WPrevLink, R);
}
}
/********************************************************************
********************************************************************/
void _K33Search_InitEdgeRec(K33SearchContext *context, int e)
{
context->E[e].noStraddle = NIL;
context->E[e].pathConnector = NIL;
}
/********************************************************************
********************************************************************/
void _K33Search_InitVertexInfo(K33SearchContext *context, int v)
{
context->VI[v].separatedDFSChildList = NIL;
context->VI[v].backArcList = NIL;
context->VI[v].mergeBlocker = NIL;
}
/********************************************************************
********************************************************************/
int _K33Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R)
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context == NULL)
return NOTOK;
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
// If R is the root of a descendant bicomp of v, we push it, but then we know the search for K3,3
// will be successful and return NONEMBEDDABLE because this condition corresponds to minor A, which
// is a K3,3. Thus, an "OK to proceed with Walkdown searching elsewhere" result cannot happen,
// so we don't have to test for it to detect if we have to pop these two back off the stack.
if (R != RootVertex)
sp_Push2(theGraph->theStack, R, 0);
// The possible results here are NONEMBEDDABLE if a K3,3 homeomorph is found, or OK if only
// a K5 was found and unblocked such that it is OK for the Walkdown to continue searching
// elsewhere. Note that the OK result can only happen if RootVertex==R since minor E can only
// happen on a child bicomp of vertex v, not a descendant bicomp.
return _SearchForK33InBicomp(theGraph, context, v, RootVertex);
}
else
{
return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R);
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
{
// For K3,3 search, we just return the edge embedding result because the
// search result has been obtained already.
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
return edgeEmbeddingResult;
}
// When not searching for K3,3, we let the superclass do the work
else
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
{
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
return OK;
}
// When not searching for K3,3, we let the superclass do the work
else
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
{
// When searching for K3,3, we ensure that theGraph is a subgraph of
// the original graph and that it contains a K3,3 homeomorph
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33)
{
int degrees[5], imageVerts[6];
if (_TestSubgraph(theGraph, origGraph) != TRUE)
{
return NOTOK;
}
if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK)
{
return NOTOK;
}
if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE)
{
return OK;
}
return NOTOK;
}
// When not searching for K3,3, we let the superclass do the work
else
{
K33SearchContext *context = NULL;
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
}
}
return NOTOK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK4Search.c 0000664 0000000 0000000 00000161424 12716155161 0024240 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphK4Search.h"
#include "graphK4Search.private.h"
extern int K4SEARCH_ID;
#include "graph.h"
/* Imported functions */
extern void _InitIsolatorContext(graphP theGraph);
extern void _ClearVisitedFlags(graphP);
extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
//extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
//extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph);
extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot);
//extern int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot);
extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType);
extern int _SetEdgeType(graphP theGraph, int u, int v);
extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
extern int _JoinBicomps(graphP theGraph);
//extern void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY);
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
extern int _OrientVerticesInEmbedding(graphP theGraph);
//extern void _InvertVertex(graphP theGraph, int V);
extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x);
extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x);
extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x);
extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant);
extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);
extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex);
extern int _SetVertexTypesForMarkingXYPath(graphP theGraph);
extern int _MarkHighestXYPath(graphP theGraph);
extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
extern int _IsolateOuterplanarityObstructionA(graphP theGraph);
//extern int _IsolateOuterplanarityObstructionB(graphP theGraph);
extern int _IsolateOuterplanarityObstructionE(graphP theGraph);
extern void _K4Search_InitEdgeRec(K4SearchContext *context, int e);
/* Private functions for K4 searching (exposed to the extension). */
int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R);
/* Private functions for K4 searching. */
int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R);
int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph);
int _K4_FindPlanarityActiveVertex(graphP theGraph, int v, int R, int prevLink, int *pW);
int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY);
void _K4_MarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A);
void _K4_UnmarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A);
int _K4_IsolateMinorA1(graphP theGraph);
int _K4_IsolateMinorA2(graphP theGraph);
int _K4_IsolateMinorB1(graphP theGraph);
int _K4_IsolateMinorB2(graphP theGraph);
int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W);
int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A);
int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A);
int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant);
int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A);
void _K4_ClearVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A);
int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A);
int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, int BicompRoot);
int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e);
int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context);
//int _MarkEdge(graphP theGraph, int x, int y);
/****************************************************************************
_SearchForK4InBicomp()
****************************************************************************/
int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R)
{
isolatorContextP IC = &theGraph->IC;
if (context == NULL)
{
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context == NULL)
return NOTOK;
}
// Begin by determining whether minor A, B or E is detected
if (_K4_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK)
return NOTOK;
// Minor A indicates the existence of K_{2,3} homeomorphs, but
// we run additional tests to see whether we can either find an
// entwined K4 homeomorph or reduce the bicomp so that the WalkDown
// is enabled to continue to resolve pertinence
if (theGraph->IC.minorType & MINORTYPE_A)
{
// Now that we know we have minor A, we can afford to orient the
// bicomp because we will either find the desired K4 or we will
// reduce the bicomp to an edge. The tests for A1 and A2 are easier
// to implement on an oriented bicomp.
// NOTE: We're in the midst of the WalkDown, so the stack may be
// non-empty, and it has to be preserved with constant cost.
// The stack will have at most 4 integers per cut vertex
// merge point, and this operation will push at most two
// integers per tree edge in the bicomp, so the stack
// will not overflow.
if (sp_GetCapacity(theGraph->theStack) < 6*theGraph->N)
return NOTOK;
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
return NOTOK;
// Case A1: Test whether there is an active vertex Z other than W
// along the external face path [X, ..., W, ..., Y]
if (_K4_FindSecondActiveVertexOnLowExtFacePath(theGraph) == TRUE)
{
// Now that we know we can find a K4, the Walkdown will not continue
// and we can do away with the stack content.
sp_ClearStack(theGraph->theStack);
// Restore the orientations of the vertices in the bicomp, then orient
// the whole embedding, so we can restore and orient the reduced paths
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK ||
_OrientVerticesInEmbedding(theGraph) != OK ||
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
// Set up to isolate K4 homeomorph
_ClearVisitedFlags(theGraph);
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
if (IC->uz < IC->v)
{
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)
return NOTOK;
}
else
{
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->z, &IC->dz) != TRUE)
return NOTOK;
}
// Isolate the K4 homeomorph
if (_K4_IsolateMinorA1(theGraph) != OK ||
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
// Indicate success by returning NONEMBEDDABLE
return NONEMBEDDABLE;
}
// Case A2: Test whether the bicomp has an XY path
// NOTE: As mentioned above, the stack is also preserved here.
// It will have at most 4 integers per cut vertex merge point,
// and this operation will push at most one integer per tree
// edge in the bicomp, so the stack will not overflow.
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
return NOTOK;
// Marking the X-Y path relies on the bicomp visited flags being cleared
if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
// NOTE: This call preserves the stack and does not overflow. There
// are at most 4 integers per cut vertex merge point, all of which
// are not in the bicomp, and this call pushes at most 3 integers
// per bicomp vertex, so the maximum stack requirement is 4N
if (_MarkHighestXYPath(theGraph) == TRUE)
{
// Now that we know we can find a K4, the Walkdown will not continue
// and we can do away with the stack content.
sp_ClearStack(theGraph->theStack);
// Restore the orientations of the vertices in the bicomp, then orient
// the whole embedding, so we can restore and orient the reduced paths
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK ||
_OrientVerticesInEmbedding(theGraph) != OK ||
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
// Set up to isolate K4 homeomorph
_ClearVisitedFlags(theGraph);
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
// Isolate the K4 homeomorph
if (_MarkHighestXYPath(theGraph) != TRUE ||
_K4_IsolateMinorA2(theGraph) != OK ||
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
// Indicate success by returning NONEMBEDDABLE
return NONEMBEDDABLE;
}
// else if there was no X-Y path, then we restore the vertex types to
// unknown (though it would suffice to do it just to R and W)
if (_ClearVertexTypeInBicomp(theGraph, R) != OK)
return NOTOK;
// Since neither A1 nor A2 is found, then we reduce the bicomp to the
// tree edge (R, W).
// NOTE: The visited flags for R and W are restored to values appropriate
// for continuing with future embedding steps
// NOTE: This method invokes several routines that use the stack, but
// all of them preserve the stack and each pushes at most one
// integer per bicomp vertex and pops all of them before returning.
// Again, this means the stack will not overflow.
if (_K4_ReduceBicompToEdge(theGraph, context, R, IC->w) != OK)
return NOTOK;
// Return OK so that the WalkDown can continue resolving the pertinence of v.
return OK;
}
// Minor B also indicates the existence of K_{2,3} homeomorphs, but
// we run additional tests to see whether we can either find an
// entwined K4 homeomorph or reduce a portion of the bicomp so that
// the WalkDown can be reinvoked on the bicomp
else if (theGraph->IC.minorType & MINORTYPE_B)
{
int a_x, a_y;
// Reality check on stack state
if (sp_NonEmpty(theGraph->theStack))
return NOTOK;
// Find the vertices a_x and a_y that are active (pertinent or future pertinent)
// and also first along the external face paths emanating from the bicomp root
if (_K4_FindPlanarityActiveVertex(theGraph, v, R, 1, &a_x) != OK ||
_K4_FindPlanarityActiveVertex(theGraph, v, R, 0, &a_y) != OK)
return NOTOK;
// Case B1: If both a_x and a_y are future pertinent, then we can stop and
// isolate a subgraph homeomorphic to K4.
gp_UpdateVertexFuturePertinentChild(theGraph, a_x, v);
gp_UpdateVertexFuturePertinentChild(theGraph, a_y, v);
if (a_x != a_y && FUTUREPERTINENT(theGraph, a_x, v) && FUTUREPERTINENT(theGraph, a_y, v))
{
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
// Set up to isolate K4 homeomorph
_ClearVisitedFlags(theGraph);
IC->x = a_x;
IC->y = a_y;
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE ||
_FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE)
return NOTOK;
// Isolate the K4 homeomorph
if (_K4_IsolateMinorB1(theGraph) != OK ||
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
// Indicate success by returning NONEMBEDDABLE
return NONEMBEDDABLE;
}
// Reality check: The bicomp with root R is pertinent, and the only
// pertinent or future pertinent vertex on the external face is a_x,
// so it must also be pertinent.
if (a_x == a_y && !PERTINENT(theGraph, a_x))
return NOTOK;
// Case B2: Determine whether there is an internal separating X-Y path for a_x or for a_y
// The method makes appropriate isolator context settings if the separator edge is found
if (_K4_FindSeparatingInternalEdge(theGraph, R, 1, a_x, &IC->w, &IC->px, &IC->py) == TRUE ||
_K4_FindSeparatingInternalEdge(theGraph, R, 0, a_y, &IC->w, &IC->py, &IC->px) == TRUE)
{
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
// Set up to isolate K4 homeomorph
_ClearVisitedFlags(theGraph);
if (PERTINENT(theGraph, IC->w))
{
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
}
else
{
IC->z = IC->w;
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)
return NOTOK;
}
// The X-Y path doesn't have to be the same one that was associated with the
// separating internal edge.
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK ||
_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
// Isolate the K4 homeomorph
if (_K4_IsolateMinorB2(theGraph) != OK ||
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
// Indicate success by returning NONEMBEDDABLE
return NONEMBEDDABLE;
}
// If K_4 homeomorph not found, make reductions along a_x and a_y paths.
if (a_x == a_y)
{
// In the special case where both paths lead to the same vertex, we can
// reduce the bicomp to a single edge, which avoids issues of reversed
// orientation between the bicomp root and the vertex.
if (_K4_ReduceBicompToEdge(theGraph, context, R, a_x) != OK)
return NOTOK;
}
else
{
// When a_x and a_y are distinct, we reduce each path from root to the vertex
if (_K4_ReducePathComponent(theGraph, context, R, 1, a_x) != OK ||
_K4_ReducePathComponent(theGraph, context, R, 0, a_y) != OK)
return NOTOK;
}
// Return OK to indicate that WalkDown processing may proceed to resolve
// more of the pertinence of this bicomp.
return OK;
}
// Minor E indicates the desired K4 homeomorph, so we isolate it and return NONEMBEDDABLE
else if (theGraph->IC.minorType & MINORTYPE_E)
{
// Reality check on stack state
if (sp_NonEmpty(theGraph->theStack))
return NOTOK;
// Impose consistent orientation on the embedding so we can then
// restore the reduced paths.
if (_OrientVerticesInEmbedding(theGraph) != OK ||
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
return NOTOK;
// Set up to isolate minor E
_ClearVisitedFlags(theGraph);
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
return NOTOK;
if (_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
// Isolate the K4 homeomorph
if (_IsolateOuterplanarityObstructionE(theGraph) != OK ||
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
return NOTOK;
// Return indication that K4 homeomorph has been found
return NONEMBEDDABLE;
}
// You never get here in an error-free implementation like this one
return NOTOK;
}
/****************************************************************************
_K4_ChooseTypeOfNonOuterplanarityMinor()
This is an overload of the function _ChooseTypeOfNonOuterplanarityMinor()
that avoids processing the whole bicomp rooted by R, e.g. to orient its
vertices or label the vertices of its external face.
This is necessary in particular because of the reduction processing on
MINORTYPE_B. When a K2,3 is found by minor B, we may not be able to find
an entangled K4, so a reduction is performed, but it only eliminates
part of the bicomp and the operations here need to avoid touching parts
of the bicomp that won't be reduced, except by a constant amount of course.
****************************************************************************/
int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R)
{
int XPrevLink=1, YPrevLink=0;
int Wx, WxPrevLink, Wy, WyPrevLink;
_InitIsolatorContext(theGraph);
theGraph->IC.v = v;
theGraph->IC.r = R;
// Reality check on data structure integrity
if (!gp_VirtualVertexInUse(theGraph, R))
return NOTOK;
// We are essentially doing a _FindActiveVertices() here, except two things:
// 1) for outerplanarity we know the first vertices along the paths from R
// are the desired vertices because no vertices are "inactive"
// 2) We have purposely not oriented the bicomp, so the XPrevLink result is
// needed to help find the pertinent vertex W
theGraph->IC.x = _GetNeighborOnExtFace(theGraph, R, &XPrevLink);
theGraph->IC.y = _GetNeighborOnExtFace(theGraph, R, &YPrevLink);
// We are essentially doing a _FindPertinentVertex() here, except two things:
// 1) It is not known whether the reduction of the path through X or the path
// through Y will enable the pertinence of W to be resolved, so it is
// necessary to perform parallel face traversal to find W with a cost no
// more than twice what it will take to resolve the W's pertinence
// (assuming we have to do a reduction rather than finding an entangled K4)
// 2) In the normal _FindPertinentVertex(), the bicomp is already oriented, so
// the "prev link" is hard coded to traverse down the X side. In this
// implementation, the bicomp is purposely not oriented, so we need to know
// XPrevLink and YPrevLink in order to set off in the correct directions.
Wx = theGraph->IC.x;
WxPrevLink = XPrevLink;
Wy = theGraph->IC.y;
WyPrevLink = YPrevLink;
theGraph->IC.w = NIL;
while (Wx != theGraph->IC.y)
{
Wx = _GetNeighborOnExtFace(theGraph, Wx, &WxPrevLink);
if (PERTINENT(theGraph, Wx))
{
theGraph->IC.w = Wx;
break;
}
Wy = _GetNeighborOnExtFace(theGraph, Wy, &WyPrevLink);
if (PERTINENT(theGraph, Wy))
{
theGraph->IC.w = Wy;
break;
}
}
if (gp_IsNotVertex(theGraph->IC.w))
return NOTOK;
// If the root copy is not a root copy of the current vertex v,
// then the Walkdown terminated on a descendant bicomp, which is Minor A.
if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v)
theGraph->IC.minorType |= MINORTYPE_A;
// If W has a pertinent child bicomp, then we've found Minor B.
// Notice this is different from planarity, in which minor B is indicated
// only if the pertinent child bicomp is also future pertinent.
else if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theGraph->IC.w)))
theGraph->IC.minorType |= MINORTYPE_B;
// The only other result is minor E (we will search for the X-Y path later)
else
theGraph->IC.minorType |= MINORTYPE_E;
return OK;
}
/****************************************************************************
_K4_FindSecondActiveVertexOnLowExtFacePath()
This method is used in the processing of obstruction A, so it can take
advantage of the bicomp being oriented beforehand.
This method determines whether there is an active vertex Z other than W on
the path [X, ..., W, ..., Y]. By active, we mean a vertex that connects
by an unembedded edge to either v or an ancestor of v. That is, a vertex
that is pertinent or future pertinent (would be pertinent in a future step
of the embedder).
****************************************************************************/
int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph)
{
int Z=theGraph->IC.r, ZPrevLink=1;
// First we test X for future pertinence only (if it were pertinent, then
// we wouldn't have been blocked up on this bicomp)
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v);
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
{
theGraph->IC.z = Z;
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
return TRUE;
}
// Now we move on to test all the vertices strictly between X and Y on
// the lower external face path, except W, for either pertinence or
// future pertinence.
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
while (Z != theGraph->IC.y)
{
if (Z != theGraph->IC.w)
{
gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v);
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
{
theGraph->IC.z = Z;
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
return TRUE;
}
else if (PERTINENT(theGraph, Z))
{
theGraph->IC.z = Z;
theGraph->IC.uz = theGraph->IC.v;
return TRUE;
}
}
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
// Now we test Y for future pertinence (same explanation as for X above)
gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v);
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
{
theGraph->IC.z = Z;
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
return TRUE;
}
// We didn't find the desired second vertex, so report FALSE
return FALSE;
}
/****************************************************************************
_K4_FindPlanarityActiveVertex()
This service routine starts out at R and heads off in the direction opposite
the prevLink to find the first "planarity active" vertex, i.e. the first one
that is pertinent or future pertinent.
****************************************************************************/
int _K4_FindPlanarityActiveVertex(graphP theGraph, int v, int R, int prevLink, int *pW)
{
int W = R, WPrevLink = prevLink;
W = _GetNeighborOnExtFace(theGraph, R, &WPrevLink);
while (W != R)
{
if (PERTINENT(theGraph, W))
{
*pW = W;
return OK;
}
else
{
gp_UpdateVertexFuturePertinentChild(theGraph, W, v);
if (FUTUREPERTINENT(theGraph, W, v))
{
*pW = W;
return OK;
}
}
W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink);
}
return NOTOK;
}
/****************************************************************************
_K4_FindSeparatingInternalEdge()
Logically, this method is similar to calling MarkHighestXYPath() to
see if there is an internal separator between R and A.
However, that method cannot be called because the bicomp is not oriented.
Because this is an outerplanarity related algorithm, there are no internal
vertices to contend with, so it is easier to inspect the internal edges
incident to each vertex internal to the path (R ... A), i.e. excluding endpoints,
to see whether any of the edges connects outside of the path [R ... A],
including endpoints.
In order to avoid adding cost to the core planarity algorithm, we'll just use
the fact that the vertex obstruction types are pre-initialized to the unmarked
state. Until we know if there is a separating internal edge, we cannot be sure
that we can isolate a K4 homeomorph, so we can't afford to initialize the whole
bicomp. The obstruction type of each vertex along the path [R ... A] is marked.
Then, for each vertex in the range (R ... A), if there is any edge that is also
incident to a vertex whose obstruction type is still unmarked, then that edge
is the desired separator edge between R and W (i.e. A), and so we save information
about it.
If the separator edge is found, then this method sets the *pW to A, and it
sets *pX and *pY values with the endpoints of the separator edge.
The visited flags of the separator edge and its endpoint vertices are not set
at this time because it is easier to set them later as part of the overall K4
homeomorph isolation.
Lastly, we restore the unmarked obstruction type settings on the path [R ... A].
Returns TRUE if separator edge found or FALSE otherwise
****************************************************************************/
int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY)
{
int Z, ZPrevLink, e, neighbor;
// Mark the vertex obstruction type settings along the path [R ... A]
_K4_MarkObstructionTypeOnExternalFacePath(theGraph, R, prevLink, A);
// Search each of the vertices in the range (R ... A)
*pX = *pY = NIL;
ZPrevLink = prevLink;
Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink);
while (Z != A)
{
// Search for a separator among the edges of Z
// It is OK to not bother skipping the external face edges, since we
// know they are marked visited and so are ignored
e = gp_GetFirstArc(theGraph, Z);
while (gp_IsArc(e))
{
neighbor = gp_GetNeighbor(theGraph, e);
if (gp_GetVertexObstructionType(theGraph, neighbor) == VERTEX_OBSTRUCTIONTYPE_UNMARKED)
{
*pW = A;
*pX = Z;
*pY = neighbor;
break;
}
e = gp_GetNextArc(theGraph, e);
}
// If we found the separator edge, then we don't need to go on
if (gp_IsVertex(*pX))
break;
// Go to the next vertex
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
// Restore the unmarked obstruction type settings on the path [R ... A]
_K4_UnmarkObstructionTypeOnExternalFacePath(theGraph, R, prevLink, A);
return gp_IsVertex(*pX) ? TRUE : FALSE;
}
/****************************************************************************
_K4_MarkObstructionTypeOnExternalFacePath()
Assumes A is a vertex along the external face of the bicomp rooted by R.
Marks the obstruction type of vertices along the path (R ... A) that begins
with R's link[1^prevLink] arc.
****************************************************************************/
void _K4_MarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A)
{
int Z, ZPrevLink;
gp_SetVertexObstructionType(theGraph, R, VERTEX_OBSTRUCTIONTYPE_MARKED);
ZPrevLink = prevLink;
Z = R;
while (Z != A)
{
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
gp_SetVertexObstructionType(theGraph, Z, VERTEX_OBSTRUCTIONTYPE_MARKED);
}
}
/****************************************************************************
_K4_UnmarkObstructionTypeOnExternalFacePath()
Assumes A is a vertex along the external face of the bicomp rooted by R.
Unmarks the obstruction type of vertices along the path (R ... A) that begins
with R's link[1^prevLink] arc.
****************************************************************************/
void _K4_UnmarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A)
{
int Z, ZPrevLink;
gp_ClearVertexObstructionType(theGraph, R);
ZPrevLink = prevLink;
Z = R;
while (Z != A)
{
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
gp_ClearVertexObstructionType(theGraph, Z);
}
}
/****************************************************************************
_K4_IsolateMinorA1()
This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get
a K_4 via the additional path from some vertex Z to the current vertex.
This path may be via some descendant of Z, and it may be a future pertinent
connection to an ancestor of the current vertex.
****************************************************************************/
int _K4_IsolateMinorA1(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (IC->uz < IC->v)
{
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->uz, IC->v) != OK)
return NOTOK;
}
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->z, IC->dz) != OK)
return NOTOK;
if (_IsolateOuterplanarityObstructionA(theGraph) != OK)
return NOTOK;
if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_K4_IsolateMinorA2()
This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get
a K_4 via an additional X-Y path within the main bicomp, which is guaranteed
to exist by the time this method is invoked.
One might think to simply invoke _MarkHighestXYPath() to obtain the path,
but the IC->px and IC->py values are already set before invoking this method,
and the bicomp is outerplanar, so the XY path is just an edge. Also, one
subcase of pattern B2 reduces to this pattern, except that the XY path is
determined by the B2 isolator.
****************************************************************************/
int _K4_IsolateMinorA2(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
// We assume the X-Y path was already marked
if (!gp_GetVertexVisited(theGraph, IC->px) || !gp_GetVertexVisited(theGraph, IC->py))
return NOTOK;
return _IsolateOuterplanarityObstructionA(theGraph);
}
/****************************************************************************
_K4_IsolateMinorB1()
It is possible to get a K_4 based on the pertinence of w, but we don't do it
that way. If we use the pertinence of w, then we have to eliminate part of
the bicomp external face, which has special cases if a_x==w or a_y==w.
Typically we would mark (r ... a_x ... w ... a_y), which works even when a_y==w,
but if instead a_x==w, then we'd have to mark (w ... a_y ... r).
Since a_x and a_y are guaranteed to be distinct, it is easier to just ignore
the pertinence of w, and instead use the lower bicomp external face path
as the connection between a_x and a_y. This includes w, but then the
isolation process is the same even if a_x==w or a_y==w. The other two
connections for a_x and a_y are to v and MAX(ux, uy).
****************************************************************************/
int _K4_IsolateMinorB1(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK)
return NOTOK;
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK)
return NOTOK;
// The path from the bicomp root to MIN(ux,uy) is marked to ensure the
// connection from the image vertices v and MAX(ux,uy) as well as the
// connection from MAX(ux,uy) through MIN(ux,uy) to (ux==MIN(ux,uy)?x:y)
if (theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK)
return NOTOK;
// This makes the following connections (a_x ... v), (a_y ... v), and
// (a_x ... w ... a_y), the last being tolerant of a_x==w or a_y==w
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK)
return NOTOK;
if (_JoinBicomps(theGraph) != OK)
return NOTOK;
if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK)
return NOTOK;
if (_AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_K4_IsolateMinorB2()
The first subcase of B2 can be reduced to outerplanarity obstruction E
The second subcase of B2 can be reduced to A2 by changing v to u
****************************************************************************/
int _K4_IsolateMinorB2(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
// First subcase, the active vertex is pertinent
if (PERTINENT(theGraph, IC->w))
{
// We assume the X-Y path was already marked
if (!gp_GetVertexVisited(theGraph, IC->px) || !gp_GetVertexVisited(theGraph, IC->py))
return NOTOK;
return _IsolateOuterplanarityObstructionE(theGraph);
}
// Second subcase, the active vertex is future pertinent
else if (FUTUREPERTINENT(theGraph, IC->w, IC->v))
{
IC->v = IC->uz;
IC->dw = IC->dz;
return _K4_IsolateMinorA2(theGraph);
}
return OK;
}
/****************************************************************************
_K4_ReduceBicompToEdge()
This method is used when reducing the main bicomp of obstruction A to a
single edge (R, W). We first delete all edges from the bicomp except
those on the DFS tree path W to R, then we reduce that DFS tree path to
a DFS tree edge.
After the reduction, the outerplanarity Walkdown traversal can continue
R to W without being blocked as was the case when R was adjacent to X and Y.
Returns OK for success, NOTOK for internal (implementation) error.
****************************************************************************/
int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W)
{
int newEdge;
if (_OrientVerticesInBicomp(theGraph, R, 0) != OK ||
_ClearVisitedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
if (theGraph->functions.fpMarkDFSPath(theGraph, R, W) != OK)
return NOTOK;
if (_K4_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK)
return NOTOK;
// Now we have to reduce the path W -> R to the DFS tree edge (R, W)
newEdge =_K4_ReducePathToEdge(theGraph, context, EDGE_TYPE_PARENT,
R, gp_GetFirstArc(theGraph, R), W, gp_GetFirstArc(theGraph, W));
if (gp_IsNotArc(newEdge))
return NOTOK;
// Finally, set the visited info state of W to unvisited so that
// the core embedder (esp. Walkup) will not have any problems.
gp_SetVertexVisitedInfo(theGraph, W, theGraph->N);
return OK;
}
/****************************************************************************
_K4_ReducePathComponent()
This method is invoked when the bicomp rooted by R contains a component
subgraph that is separable from the bicomp by the 2-cut (R, A). The K_4
homeomorph isolator will have processed a significant fraction of the
component, and so it must be reduced to an edge to ensure that said
processing happens at most once on the component (except for future
operations that are bound to linear time in total by other arguments).
Because the bicomp is an outerplanar embedding, the component is known to
consists of an external face path plus some internal edges that are parallel
to that path. Otherwise, it wouldn't be separable by the 2-cut (R, A).
The goal of this method is to reduce the component to the edge (R, A). This
is done in such a way that, if the reduction must be restored, the DFS tree
structure connecting the restored vertices is retained.
The first step is to ensure that (R, A) is not already just an edge, in which
case no reduction is needed. This can occur if A is future pertinent.
Assuming a non-trivial reduction component, the next step is to determine
the DFS tree structure within the component. Because it is separable by the
2-cut (R, A), there are only two cases:
Case 1: The DFS tree path from A to R is within the reduction component.
In this case, the DFS tree path is marked, the remaining edges of the
reduction component are eliminated, and then the DFS tree path is reduced to
the the tree edge (R, A).
Note that the reduction component may also contain descendants of A as well
as vertices that are descendant to R but are neither ancestors nor
descendants of A. This depends on where the tree edge from R meets the
external face path (R ... A). However, the reduction component can only
contribute one path to any future K_4, so it suffices to preserve only the
DFS tree path (A --> R).
Case 2: The DFS tree path from A to R is not within the reduction component.
In this case, the external face edge from R leads to a descendant D of A.
We mark that back edge (R, D) plus the DFS tree path (D --> A). The
remaining edges of the reduction component can be removed, and then the
path (R, D, ..., A) is reduced to the edge (R, A).
For the sake of contradiction, suppose that only part of the DFS tree path
from A to R were contained by the reduction component. Then, a DFS tree edge
would have to exit the reduction component and connect to some vertex not
on the external face path (R, ..., A). This contradicts the assumption that
the reduction subgraph is separable from the bicomp by the 2-cut (R, A).
Returns OK for success, NOTOK for internal (implementation) error.
****************************************************************************/
int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A)
{
int e_R, e_A, Z, ZPrevLink, edgeType, invertedFlag=0;
// Check whether the external face path (R, ..., A) is just an edge
e_R = gp_GetArc(theGraph, R, 1^prevLink);
if (gp_GetNeighbor(theGraph, e_R) == A)
return OK;
// Check for Case 1: The DFS tree path from A to R is within the reduction component
if (_K4_TestPathComponentForAncestor(theGraph, R, prevLink, A))
{
_K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A);
if (theGraph->functions.fpMarkDFSPath(theGraph, R, A) != OK)
return NOTOK;
edgeType = EDGE_TYPE_PARENT;
invertedFlag = _K4_GetCumulativeOrientationOnDFSPath(theGraph, R, A);
}
// Otherwise Case 2: The DFS tree path from A to R is not within the reduction component
else
{
_K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A);
Z = gp_GetNeighbor(theGraph, e_R);
gp_SetEdgeVisited(theGraph, e_R);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e_R));
if (theGraph->functions.fpMarkDFSPath(theGraph, A, Z) != OK)
return NOTOK;
edgeType = EDGE_TYPE_BACK;
}
// The path to be kept/reduced is marked, so the other edges can go
if (_K4_DeleteUnmarkedEdgesInPathComponent(theGraph, R, prevLink, A) != OK)
return NOTOK;
// Clear all the visited flags for safety, except the vertices R and A
// will remain in the embedding, and the core embedder (Walkup) uses a
// value greater than the current vertex to indicate an unvisited vertex
_K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A);
gp_SetVertexVisitedInfo(theGraph, A, theGraph->N);
// Find the component's remaining edges e_A and e_R incident to A and R
ZPrevLink = prevLink;
Z = R;
while (Z != A)
{
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
e_A = gp_GetArc(theGraph, A, ZPrevLink);
e_R = gp_GetArc(theGraph, R, 1^prevLink);
// Reduce the path (R ... A) to an edge
e_R = _K4_ReducePathToEdge(theGraph, context, edgeType, R, e_R, A, e_A);
if (gp_IsNotArc(e_R))
return NOTOK;
// Preserve the net orientation along the DFS path in the case of a tree edge
if (gp_GetEdgeType(theGraph, e_R) == EDGE_TYPE_CHILD)
{
if (invertedFlag)
gp_SetEdgeFlagInverted(theGraph, e_R);
}
return OK;
}
/********************************************************************
Edge deletion that occurs during a reduction or restoration of a
reduction is augmented by clearing the K_4 search-specific
data members. This is augmentation is not needed in the delete edge
operations that happen once a K_4 homeomorph has been found and
marked for isolation.
********************************************************************/
int _K4_DeleteEdge(graphP theGraph, K4SearchContext *context, int e, int nextLink)
{
_K4Search_InitEdgeRec(context, e);
_K4Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e));
return gp_DeleteEdge(theGraph, e, nextLink);
}
/********************************************************************
_K4_DeleteUnmarkedEdgesInBicomp()
This function deletes from a given biconnected component all edges
whose visited member is zero.
The stack is used but preserved. In debug mode, NOTOK can result if
there is a stack overflow. This method pushes at most one integer
per vertex in the bicomp.
This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls
the overloaded _K4_DeleteEdge() rather than gp_DeleteEdge()
Returns OK on success, NOTOK on implementation failure
********************************************************************/
int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, int BicompRoot)
{
int V, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
e = gp_GetFirstArc(theGraph, V);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetEdgeVisited(theGraph, e)
? gp_GetNextArc(theGraph, e)
: _K4_DeleteEdge(theGraph, context, e, 0);
}
}
return OK;
}
/****************************************************************************
_K4_GetCumulativeOrientationOnDFSPath()
****************************************************************************/
int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant)
{
int e, parent;
int invertedFlag=0;
/* If we are marking from a root vertex upward, then go up to the parent
copy before starting the loop */
if (gp_IsVirtualVertex(theGraph, descendant))
descendant = gp_GetPrimaryVertexFromRoot(theGraph, descendant);
while (descendant != ancestor)
{
if (gp_IsNotVertex(descendant))
return NOTOK;
// If we are at a bicomp root, then ascend to its parent copy
if (gp_IsVirtualVertex(theGraph, descendant))
{
parent = gp_GetPrimaryVertexFromRoot(theGraph, descendant);
}
// If we are on a regular, non-virtual vertex then get the edge to the parent
else
{
// Scan the edges for the one marked as the DFS parent
parent = NIL;
e = gp_GetFirstArc(theGraph, descendant);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
{
parent = gp_GetNeighbor(theGraph, e);
break;
}
e = gp_GetNextArc(theGraph, e);
}
// If the edge to the parent vertex was not found, then the data structure is corrupt
if (gp_IsNotVertex(parent))
return NOTOK;
// Add the inversion flag on the child arc to the cumulative result
e = gp_GetTwinArc(theGraph, e);
if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_CHILD || gp_GetNeighbor(theGraph, e) != descendant)
return NOTOK;
invertedFlag ^= gp_GetEdgeFlagInverted(theGraph, e);
}
// Hop to the parent and reiterate
descendant = parent;
}
return invertedFlag;
}
/****************************************************************************
_K4_TestPathComponentForAncestor()
Tests the external face path between R and A for a DFS ancestor of A.
Returns TRUE if found, FALSE otherwise.
****************************************************************************/
int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A)
{
int Z, ZPrevLink;
ZPrevLink = prevLink;
Z = R;
while (Z != A)
{
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
if (Z < A)
return TRUE;
}
return FALSE;
}
/****************************************************************************
_K4_ClearVisitedInPathComponent()
There is a subcomponent of the bicomp rooted by R that is separable by the
2-cut (R, A). The component contains the external face path from R to A.
The 1^prevLink arc of R is contained in that path (i.e. the first arc if
prevLink indicates the last, or the last arc if prevLink indicates the first).
The prevLink is passed because _GetNeighborOnExtFace() uses the
opposing link to traverse to the "next" vertex.
All vertices in this desired component are along the external face, so we
traverse along the external face vertices strictly between R and A and
clear all the visited flags of the edges and their incident vertices.
Note that the vertices along the path (R ... A) only have edges incident
to each other and to R and A because the component is separable by the
(R, A)-cut.
****************************************************************************/
void _K4_ClearVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A)
{
int Z, ZPrevLink, e;
ZPrevLink = prevLink;
Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink);
while (Z != A)
{
gp_ClearVertexVisited(theGraph, Z);
e = gp_GetFirstArc(theGraph, Z);
while (gp_IsArc(e))
{
gp_ClearEdgeVisited(theGraph, e);
gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
}
/****************************************************************************
_K4_DeleteUnmarkedEdgesInPathComponent()
There is a subcomponent of the bicomp rooted by R that is separable by the
2-cut (R, A) and contains the external face path from R to A that includes
the arc gp_GetArc(theGraph, R, 1^prevLink), which is the first arc traversed
by _GetNeighborOnExtFace(..., &prevLink).
The edges in the component have been marked unvisited except for a path we
intend to preserve. This routine deletes the unvisited edges.
NOTE: This reduction invalidates the short-circuit extFace data structure,
but it will be repaired for use by WalkUp and WalkDown when the path
component reduction is completed.
Returns OK on success, NOTOK on internal error
****************************************************************************/
int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A)
{
int Z, ZPrevLink, e;
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context == NULL)
return NOTOK;
// We need to use the stack to store up the edges we're going to delete.
// We want to make sure there is enough stack capacity to handle it,
// which is of course true because the stack is supposed to be empty.
// We're doing a reduction on a bicomp on which the WalkDown has completed,
// so the stack contains no bicomp roots to merge.
if (sp_NonEmpty(theGraph->theStack))
return NOTOK;
// Traverse all vertices internal to the path (R ... A) and push
// all non-visited edges
ZPrevLink = prevLink;
Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink);
while (Z != A)
{
e = gp_GetFirstArc(theGraph, Z);
while (gp_IsArc(e))
{
// The comparison of e to its twin is a useful way of ensuring we
// don't push the edge twice, which is of course only applicable
// when processing an edge whose endpoints are both internal to
// the path (R ... A)
if (!gp_GetEdgeVisited(theGraph, e) &&
(e < gp_GetTwinArc(theGraph, e) ||
gp_GetNeighbor(theGraph, e) == R || gp_GetNeighbor(theGraph, e) == A))
{
sp_Push(theGraph->theStack, e);
}
e = gp_GetNextArc(theGraph, e);
}
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
// Delete all the non-visited edges
while (sp_NonEmpty(theGraph->theStack))
{
sp_Pop(theGraph->theStack, e);
_K4_DeleteEdge(theGraph, context, e, 0);
}
return OK;
}
/****************************************************************************
_K4_ReducePathToEdge()
Returns an arc of the edge created on success, a non-arc (NOTOK) on failure
On success, the arc is in the adjacency list of R. The result can be tested
for success or failure using comparison with NIL (non-NIL being success)
****************************************************************************/
int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A)
{
// Find out the links used in vertex R for edge e_R and in vertex A for edge e_A
int Rlink = gp_GetFirstArc(theGraph, R) == e_R ? 0 : 1;
int Alink = gp_GetFirstArc(theGraph, A) == e_A ? 0 : 1;
// If the path is more than a single edge, then it must be reduced to an edge.
// Note that even if the path is a single edge, the external face data structure
// must still be modified since many edges connecting the external face have
// been deleted
if (gp_GetNeighbor(theGraph, e_R) != A)
{
int v_R, v_A;
// Prepare for removing each of the two edges that join the path to the bicomp by
// restoring it if it is a reduction edge (a constant time operation)
if (gp_IsVertex(context->E[e_R].pathConnector))
{
if (_K4_RestoreReducedPath(theGraph, context, e_R) != OK)
return NOTOK;
e_R = gp_GetArc(theGraph, R, Rlink);
}
if (gp_IsVertex(context->E[e_A].pathConnector))
{
if (_K4_RestoreReducedPath(theGraph, context, e_A) != OK)
return NOTOK;
e_A = gp_GetArc(theGraph, A, Alink);
}
// Save the vertex neighbors of R and A indicated by e_R and e_A for
// later use in setting up the path connectors.
v_R = gp_GetNeighbor(theGraph, e_R);
v_A = gp_GetNeighbor(theGraph, e_A);
// Now delete the two edges that join the path to the bicomp.
_K4_DeleteEdge(theGraph, context, e_R, 0);
_K4_DeleteEdge(theGraph, context, e_A, 0);
// Now add a single edge to represent the path
// We use 1^Rlink, for example, because Rlink was the link from R that indicated e_R,
// so 1^Rlink is the link that indicated e_R in the other arc that was adjacent to e_R.
// We want gp_InsertEdge to place the new arc where e_R was in R's adjacency list
gp_InsertEdge(theGraph, R, gp_GetArc(theGraph, R, Rlink), 1^Rlink,
A, gp_GetArc(theGraph, A, Alink), 1^Alink);
// Now set up the path connectors so the original path can be recovered if needed.
e_R = gp_GetArc(theGraph, R, Rlink);
context->E[e_R].pathConnector = v_R;
e_A = gp_GetArc(theGraph, A, Alink);
context->E[e_A].pathConnector = v_A;
// Also, set the reduction edge's type to preserve the DFS tree structure
gp_SetEdgeType(theGraph, e_R, _ComputeArcType(theGraph, R, A, edgeType));
gp_SetEdgeType(theGraph, e_A, _ComputeArcType(theGraph, A, R, edgeType));
}
// Set the external face data structure
gp_SetExtFaceVertex(theGraph, R, Rlink, A);
gp_SetExtFaceVertex(theGraph, A, Alink, R);
// If the edge represents an entire bicomp, then more external face
// settings are needed.
if (gp_GetFirstArc(theGraph, R) == gp_GetLastArc(theGraph, R))
{
gp_SetExtFaceVertex(theGraph, R, 1^Rlink, A);
gp_SetExtFaceVertex(theGraph, A, 1^Alink, R);
}
return e_R;
}
/****************************************************************************
_K4_RestoreReducedPath()
Given an edge record of an edge used to reduce a path, we want to restore
the path in constant time.
The path may contain more reduction edges internally, but we do not
search for and process those since it would violate the constant time
bound required of this function.
Note that we don't bother amending the external face data structure because
a reduced path is only restored when it will shortly be reduced again or
when we don't really need the external face data structure anymore.
Return OK on success, NOTOK on failure
****************************************************************************/
int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e)
{
int eTwin, u, v, w, x;
int e0, e1, eTwin0, eTwin1;
if (gp_IsNotVertex(context->E[e].pathConnector))
return OK;
eTwin = gp_GetTwinArc(theGraph, e);
u = gp_GetNeighbor(theGraph, eTwin);
v = context->E[e].pathConnector;
w = context->E[eTwin].pathConnector;
x = gp_GetNeighbor(theGraph, e);
// Get the locations of the EdgeRecs between which the new EdgeRecs
// must be added in order to reconnect the path parallel to the edge.
e0 = gp_GetNextArc(theGraph, e);
e1 = gp_GetPrevArc(theGraph, e);
eTwin0 = gp_GetNextArc(theGraph, eTwin);
eTwin1 = gp_GetPrevArc(theGraph, eTwin);
// We first delete the edge represented by e and eTwin. We do so before
// restoring the path to ensure we do not exceed the maximum arc capacity.
_K4_DeleteEdge(theGraph, context, e, 0);
// Now we add the two edges to reconnect the reduced path represented
// by the edge [e, eTwin]. The edge record in u is added between e0 and e1.
// Likewise, the new edge record in x is added between eTwin0 and eTwin1.
if (gp_IsArc(e0))
{
if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK)
return NOTOK;
}
else
{
if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK)
return NOTOK;
}
if (gp_IsArc(eTwin0))
{
if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK)
return NOTOK;
}
else
{
if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK)
return NOTOK;
}
// Set the types of the newly added edges. In both cases, the first of the two
// vertex parameters is known to be degree 2 because they are internal to the
// path being restored, so this operation is constant time.
if (_SetEdgeType(theGraph, v, u) != OK || _SetEdgeType(theGraph, w, x) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_K4_RestoreAndOrientReducedPaths()
This function searches the embedding for any edges that are specially marked
as being representative of a path that was previously reduced to a single edge
by _ReducePathToEdge(). This method restores the path by replacing the edge
with the path.
Note that the new path may contain more reduction edges, and these will be
iteratively expanded by the outer for loop. Equally, a reduced path may
be restored into a path that itself is a reduction path that will only be
attached to the embedding by some future step of the outer loop.
The vertices along the path being restored must be given a consistent
orientation with the endpoints. It is expected that the embedding
will have been oriented prior to this operation.
****************************************************************************/
int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context)
{
int EsizeOccupied, e, eTwin, u, v, w, x, visited;
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;)
{
if (gp_IsVertex(context->E[e].pathConnector))
{
visited = gp_GetEdgeVisited(theGraph, e);
eTwin = gp_GetTwinArc(theGraph, e);
u = gp_GetNeighbor(theGraph, eTwin);
v = context->E[e].pathConnector;
w = context->E[eTwin].pathConnector;
x = gp_GetNeighbor(theGraph, e);
if (_K4_RestoreReducedPath(theGraph, context, e) != OK)
return NOTOK;
// If the path is on the external face, orient it
if (gp_GetNeighbor(theGraph, gp_GetFirstArc(theGraph, u)) == v ||
gp_GetNeighbor(theGraph, gp_GetLastArc(theGraph, u)) == v)
{
// Reality check: ensure the path is connected to the
// external face at both vertices.
if (gp_GetNeighbor(theGraph, gp_GetFirstArc(theGraph, x)) != w &&
gp_GetNeighbor(theGraph, gp_GetLastArc(theGraph, x)) != w)
return NOTOK;
if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK)
return NOTOK;
}
if (visited)
{
if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK)
return NOTOK;
}
else
{
if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK)
return NOTOK;
}
}
else e+=2;
}
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK4Search.h 0000664 0000000 0000000 00000000642 12716155161 0024237 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_K4SEARCH_H
#define GRAPH_K4SEARCH_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graphStructures.h"
#ifdef __cplusplus
extern "C" {
#endif
#define K4SEARCH_NAME "K4Search"
int gp_AttachK4Search(graphP theGraph);
int gp_DetachK4Search(graphP theGraph);
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK4Search.private.h 0000664 0000000 0000000 00000002761 12716155161 0025714 0 ustar 00root root 0000000 0000000 #ifndef GRAPH_K4SEARCH_PRIVATE_H
#define GRAPH_K4SEARCH_PRIVATE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graph.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Additional equipment for each EdgeRec
pathConnector:
Used in the edge records (arcs) of a reduction edge to indicate the
endpoints of a path that has been reduced from (removed from) the
embedding so that the search for a K4 can continue.
We only need a pathConnector because we reduce subgraphs that are
separable by a 2-cut, so they can contribute at most one path to a
subgraph homeomorphic to K4, if one is indeed found. Thus, we first
delete all edges except for the desired path(s), then we reduce any
retained path to an edge.
*/
typedef struct
{
int pathConnector;
} K4Search_EdgeRec;
typedef K4Search_EdgeRec * K4Search_EdgeRecP;
/* Additional equipment for each vertex: None */
typedef struct
{
// Helps distinguish initialize from re-initialize
int initialized;
// The graph that this context augments
graphP theGraph;
// Parallel array for additional edge level equipment
K4Search_EdgeRecP E;
// Overloaded function pointers
graphFunctionTable functions;
// Internal variable for converting a tail recursion into a simple loop
int handlingBlockedBicomp;
} K4SearchContext;
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphK4Search_Extensions.c 0000664 0000000 0000000 00000042067 12716155161 0026460 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "graphK4Search.private.h"
#include "graphK4Search.h"
extern int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R);
extern int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts,
int *degrees, int *imageVerts);
extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,
int *imageVerts, int maxNumImageVerts);
extern int _TestSubgraph(graphP theSubgraph, graphP theGraph);
/* Forward declarations of local functions */
void _K4Search_ClearStructures(K4SearchContext *context);
int _K4Search_CreateStructures(K4SearchContext *context);
int _K4Search_InitStructures(K4SearchContext *context);
void _K4Search_InitEdgeRec(K4SearchContext *context, int e);
/* Forward declarations of overloading functions */
int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R);
int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
int _K4Search_InitGraph(graphP theGraph, int N);
void _K4Search_ReinitializeGraph(graphP theGraph);
int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);
/* Forward declarations of functions used by the extension system */
void *_K4Search_DupContext(void *pContext, void *theGraph);
void _K4Search_FreeContext(void *);
/****************************************************************************
* K4SEARCH_ID - the variable used to hold the integer identifier for this
* extension, enabling this feature's extension context to be distinguished
* from other features' extension contexts that may be attached to a graph.
****************************************************************************/
int K4SEARCH_ID = 0;
/****************************************************************************
gp_AttachK4Search()
This function adjusts the graph data structure to attach the K4 search
feature.
****************************************************************************/
int gp_AttachK4Search(graphP theGraph)
{
K4SearchContext *context = NULL;
// If the K4 search feature has already been attached to the graph,
// then there is no need to attach it again
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context != NULL)
{
return OK;
}
// Allocate a new extension context
context = (K4SearchContext *) malloc(sizeof(K4SearchContext));
if (context == NULL)
{
return NOTOK;
}
// First, tell the context that it is not initialized
context->initialized = 0;
// Save a pointer to theGraph in the context
context->theGraph = theGraph;
// Put the overload functions into the context function table.
// gp_AddExtension will overload the graph's functions with these, and
// return the base function pointers in the context function table
memset(&context->functions, 0, sizeof(graphFunctionTable));
context->functions.fpHandleBlockedBicomp = _K4Search_HandleBlockedBicomp;
context->functions.fpEmbedPostprocess = _K4Search_EmbedPostprocess;
context->functions.fpCheckEmbeddingIntegrity = _K4Search_CheckEmbeddingIntegrity;
context->functions.fpCheckObstructionIntegrity = _K4Search_CheckObstructionIntegrity;
context->functions.fpInitGraph = _K4Search_InitGraph;
context->functions.fpReinitializeGraph = _K4Search_ReinitializeGraph;
context->functions.fpEnsureArcCapacity = _K4Search_EnsureArcCapacity;
_K4Search_ClearStructures(context);
// Store the K4 search context, including the data structure and the
// function pointers, as an extension of the graph
if (gp_AddExtension(theGraph, &K4SEARCH_ID, (void *) context,
_K4Search_DupContext, _K4Search_FreeContext,
&context->functions) != OK)
{
_K4Search_FreeContext(context);
return NOTOK;
}
// Create the K4-specific structures if the size of the graph is known
// Attach functions are always invoked after gp_New(), but if a graph
// extension must be attached before gp_Read(), then the attachment
// also happens before gp_InitGraph(), which means N==0.
// However, sometimes a feature is attached after gp_InitGraph(), in
// which case N > 0
if (theGraph->N > 0)
{
if (_K4Search_CreateStructures(context) != OK ||
_K4Search_InitStructures(context) != OK)
{
_K4Search_FreeContext(context);
return NOTOK;
}
}
return OK;
}
/********************************************************************
gp_DetachK4Search()
********************************************************************/
int gp_DetachK4Search(graphP theGraph)
{
return gp_RemoveExtension(theGraph, K4SEARCH_ID);
}
/********************************************************************
_K4Search_ClearStructures()
********************************************************************/
void _K4Search_ClearStructures(K4SearchContext *context)
{
if (!context->initialized)
{
// Before initialization, the pointers are stray, not NULL
// Once NULL or allocated, free() or LCFree() can do the job
context->E = NULL;
context->handlingBlockedBicomp = FALSE;
context->initialized = 1;
}
else
{
if (context->E != NULL)
{
free(context->E);
context->E = NULL;
}
context->handlingBlockedBicomp = FALSE;
}
}
/********************************************************************
_K4Search_CreateStructures()
Create uninitialized structures for the vertex and edge levels, and
initialized structures for the graph level
********************************************************************/
int _K4Search_CreateStructures(K4SearchContext *context)
{
int Esize = gp_EdgeIndexBound(context->theGraph);
if (context->theGraph->N <= 0)
return NOTOK;
if ((context->E = (K4Search_EdgeRecP) malloc(Esize*sizeof(K4Search_EdgeRec))) == NULL ||
0)
{
return NOTOK;
}
return OK;
}
/********************************************************************
_K4Search_InitStructures()
********************************************************************/
int _K4Search_InitStructures(K4SearchContext *context)
{
#if NIL == 0 || NIL == -1
memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K4Search_EdgeRec));
#else
int e, Esize;
Esize = gp_EdgeIndexBound(context->theGraph);
for (e = gp_GetFirstEdge(context->theGraph); e < Esize; e++)
_K4Search_InitEdgeRec(context, e);
#endif
return OK;
}
/********************************************************************
********************************************************************/
int _K4Search_InitGraph(graphP theGraph, int N)
{
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context == NULL)
return NOTOK;
theGraph->N = N;
theGraph->NV = N;
if (theGraph->arcCapacity == 0)
theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N;
if (_K4Search_CreateStructures(context) != OK ||
_K4Search_InitStructures(context) != OK)
return NOTOK;
context->functions.fpInitGraph(theGraph, N);
return OK;
}
/********************************************************************
********************************************************************/
void _K4Search_ReinitializeGraph(graphP theGraph)
{
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context != NULL)
{
// Reinitialize the graph
context->functions.fpReinitializeGraph(theGraph);
// Do the reinitialization that is specific to this module
_K4Search_InitStructures(context);
}
}
/********************************************************************
The current implementation does not support an increase of arc
(edge record) capacity once the extension is attached to the graph
data structure. This is only due to not being necessary to support.
For now, it is easy to ensure the correct capacity before attaching
the extension, but support could be added later if there is some
reason to do so.
********************************************************************/
int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)
{
return NOTOK;
}
/********************************************************************
_K4Search_DupContext()
********************************************************************/
void *_K4Search_DupContext(void *pContext, void *theGraph)
{
K4SearchContext *context = (K4SearchContext *) pContext;
K4SearchContext *newContext = (K4SearchContext *) malloc(sizeof(K4SearchContext));
if (newContext != NULL)
{
int Esize = gp_EdgeIndexBound((graphP) theGraph);
*newContext = *context;
newContext->theGraph = (graphP) theGraph;
newContext->initialized = 0;
_K4Search_ClearStructures(newContext);
if (((graphP) theGraph)->N > 0)
{
if (_K4Search_CreateStructures(newContext) != OK)
{
_K4Search_FreeContext(newContext);
return NULL;
}
memcpy(newContext->E, context->E, Esize*sizeof(K4Search_EdgeRec));
}
}
return newContext;
}
/********************************************************************
_K4Search_FreeContext()
********************************************************************/
void _K4Search_FreeContext(void *pContext)
{
K4SearchContext *context = (K4SearchContext *) pContext;
_K4Search_ClearStructures(context);
free(pContext);
}
/********************************************************************
********************************************************************/
void _K4Search_InitEdgeRec(K4SearchContext *context, int e)
{
context->E[e].pathConnector = NIL;
}
/********************************************************************
_K4Search_HandleBlockedBicomp()
Returns OK if no K4 homeomorph found and blockage cleared (OK to
proceed with Walkdown embedding)
NONEMBEDDABLE if K4 homeomorph found, and Walkdown embedding
should be terminated.
NOTOK on internal error
********************************************************************/
int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R)
{
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context == NULL)
return NOTOK;
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
{
int RetVal = OK;
// If invoked on a descendant bicomp, then we push its root then search once
// since not finding a K4 homeomorph will also clear the blockage and allow
// the Walkdown to continue walking down
if (R != RootVertex)
{
sp_Push2(theGraph->theStack, R, 0);
if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, R)) == OK)
{
// If the Walkdown will be told it is OK to continue, then we have to take the descendant
// bicomp root back off the stack so the Walkdown can try to descend to it again.
int dummy;
sp_Pop2(theGraph->theStack, R, dummy);
// And we have to clear the indicator of the minor A that was reduced, since it was eliminated.
theGraph->IC.minorType = 0;
}
}
// Otherwise, if invoked on a child bicomp rooted by a virtual copy of v,
// then we search for a K4 homeomorph, and if OK is returned, then that indicates
// the blockage has been cleared and it is OK to Walkdown the bicomp.
// But the Walkdown finished, already, so we launch it again.
// If the Walkdown returns OK then all forward arcs were embedded. If NONEMBEDDABLE
// is returned, then the bicomp got blocked again, so we have to reiterate the K4 search
else
{
// If Walkdown has recursively called this handler on the bicomp rooted by RootVertex,
// then it is still blocked, so we just return NONEMBEDDABLE, which causes Walkdown to
// return to the loop below and signal that the loop should invoke the Walkdown again.
if (context->handlingBlockedBicomp)
return NONEMBEDDABLE;
context->handlingBlockedBicomp = TRUE;
do {
// Detect whether bicomp can be used to find a K4 homeomorph. It it does, then
// it returns NONEMBEDDABLE so we break the search because we found the desired K4
// If OK is returned, then the blockage was cleared and it is OK to Walkdown again.
if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, RootVertex)) != OK)
break;
// Walkdown again to embed more edges. If Walkdown returns OK, then all remaining
// edges to its descendants are embedded, so we'll get out of this loop. If Walkdown
// detects that it still has not embedded all the edges to descendants of the bicomp's
// root edge child, then Walkdown calls this routine again, and the above non-reentrancy
// code returns NONEMBEDDABLE, causing this loop to search again for a K4.
theGraph->IC.minorType = 0;
RetVal = theGraph->functions.fpWalkDown(theGraph, v, RootVertex);
// Except if the Walkdown returns NONEMBEDDABLE due to finding a K4 homeomorph entangled
// with a descendant bicomp (the R != RootVertex case above), then it was found
// entangled with Minor A, so we can stop the search if minor A is detected
if (theGraph->IC.minorType & MINORTYPE_A)
break;
} while (RetVal == NONEMBEDDABLE);
context->handlingBlockedBicomp = FALSE;
}
return RetVal;
}
else
{
return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R);
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
{
// For K4 search, we just return the edge embedding result because the
// search result has been obtained already.
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
{
return edgeEmbeddingResult;
}
// When not searching for K4, we let the superclass do the work
else
{
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
{
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
{
return OK;
}
// When not searching for K4, we let the superclass do the work
else
{
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);
}
}
return NOTOK;
}
/********************************************************************
********************************************************************/
int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
{
// When searching for K4, we ensure that theGraph is a subgraph of
// the original graph and that it contains a K4 homeomorph
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
{
int degrees[4], imageVerts[4];
if (_TestSubgraph(theGraph, origGraph) != TRUE)
return NOTOK;
if (_getImageVertices(theGraph, degrees, 3, imageVerts, 4) != OK)
return NOTOK;
if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE)
{
return OK;
}
return NOTOK;
}
// When not searching for K4, we let the superclass do the work
else
{
K4SearchContext *context = NULL;
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
if (context != NULL)
{
return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
}
}
return NOTOK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphNonplanar.c 0000664 0000000 0000000 00000070063 12716155161 0024562 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#define GRAPHNONPLANAR_C
#include "graph.h"
/* Imported functions */
extern void _InitIsolatorContext(graphP theGraph);
extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot);
extern int _HideInternalEdges(graphP theGraph, int vertex);
extern int _RestoreInternalEdges(graphP theGraph, int stackBottom);
//extern int _OrientVerticesInEmbedding(graphP theGraph);
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
/* Private functions (exported to system) */
int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R);
int _InitializeNonplanarityContext(graphP theGraph, int v, int R);
int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY);
int _FindPertinentVertex(graphP theGraph);
int _SetVertexTypesForMarkingXYPath(graphP theGraph);
int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom);
int _MarkHighestXYPath(graphP theGraph);
int _MarkZtoRPath(graphP theGraph);
int _FindFuturePertinenceBelowXYPath(graphP theGraph);
/****************************************************************************
_ChooseTypeOfNonplanarityMinor()
****************************************************************************/
int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R)
{
int X, Y, W, Px, Py, Z;
/* Create the initial non-planarity minor state in the isolator context */
if (_InitializeNonplanarityContext(theGraph, v, R) != OK)
return NOTOK;
R = theGraph->IC.r;
X = theGraph->IC.x;
Y = theGraph->IC.y;
W = theGraph->IC.w;
/* If the root copy is not a root copy of the current vertex v,
then the Walkdown terminated because it couldn't find
a viable path along a child bicomp, which is Minor A. */
if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v)
{
theGraph->IC.minorType |= MINORTYPE_A;
return OK;
}
/* If W has a pertinent and future pertinent child bicomp, then we've found Minor B */
if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W)))
{
if (gp_GetVertexLowpoint(theGraph, gp_GetVertexLastPertinentRootChild(theGraph, W)) < v)
{
theGraph->IC.minorType |= MINORTYPE_B;
return OK;
}
}
/* Find the highest obstructing X-Y path */
if (_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
Px = theGraph->IC.px;
Py = theGraph->IC.py;
/* If either point of attachment is 'high' (P_x closer to R than X
or P_y closer to R than Y along external face), then we've
matched Minor C. */
if (gp_GetVertexObstructionType(theGraph, Px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW ||
gp_GetVertexObstructionType(theGraph, Py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW)
{
theGraph->IC.minorType |= MINORTYPE_C;
return OK;
}
/* For Minor D, we search for a path from an internal
vertex Z along the X-Y path up to the root R of the bicomp. */
if (_MarkZtoRPath(theGraph) != OK)
return NOTOK;
if (gp_IsVertex(theGraph->IC.z))
{
theGraph->IC.minorType |= MINORTYPE_D;
return OK;
}
/* For Minor E, we search for an future pertinent vertex Z
below the points of attachment of the X-Y path */
Z = _FindFuturePertinenceBelowXYPath(theGraph);
if (gp_IsVertex(Z))
{
theGraph->IC.z = Z;
theGraph->IC.minorType |= MINORTYPE_E;
return OK;
}
return NOTOK;
}
/****************************************************************************
_InitializeNonplanarityContext()
This method finds the stopping vertices X and Y, and the pertinent vertex W
of a bicomp rooted by vertex R.
If R is NIL, the routine first determines which bicomp produced non-planarity
condition. If the stack is non-empty, then R is on the top of the stack.
Otherwise, an unembedded fwdArc from the fwdArcList of vertex v is used in
combination with the sortedDFSChildList of v to determine R.
If the parameter R was not NIL, then this method assumes it must operate
only on the bicomp rooted by R, and it also assumes that the caller has
not cleared the visited flags in the bicomp, so they are cleared.
This routine imparts consistent orientation to all vertices in bicomp R
since several subroutines count on this. The edge signs are preserved so that
the original orientations of all vertices can be restored. If the vertices
of the embedding are already consistently oriented, then this operation
simply has no effect.
Finally, in the bicomp R, the vertex types of all non-root vertices on the
external face are classified according to whether or not they are closer to
the root R than X and Y along the external face paths (R X W) and (R Y W).
****************************************************************************/
int _InitializeNonplanarityContext(graphP theGraph, int v, int R)
{
// Blank out the isolator context, then assign the input graph reference
// and the current vertext v into the context.
_InitIsolatorContext(theGraph);
theGraph->IC.v = v;
// The bicomp root provided was the one on which the WalkDown was performed,
// but in the case of Minor A, the central bicomp of the minor is at the top
// of the stack, so R must be changed to that value.
if (sp_NonEmpty(theGraph->theStack))
{
int dummy;
sp_Pop2(theGraph->theStack, R, dummy);
}
theGraph->IC.r = R;
// A number of subroutines require the main bicomp of the minor to be
// consistently oriented and its visited flags clear.
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
return NOTOK;
if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
// Now we find the active vertices along both external face paths
// extending from R.
_FindActiveVertices(theGraph, R, &theGraph->IC.x, &theGraph->IC.y);
// Now, we obtain the pertinent vertex W on the lower external face
// path between X and Y (that path that does not include R).
theGraph->IC.w = _FindPertinentVertex(theGraph);
// Now we can classify the vertices along the external face of the bicomp
// rooted at R as 'high RXW', 'low RXW', 'high RXY', 'low RXY'
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
return NOTOK;
// All work is done, so return success
return OK;
}
/********************************************************************
_GetNeighborOnExtFace()
Each vertex contains two 'link' index pointers that indicate the
first and last adjacency list arc. If the vertex is on the external face,
then these two arcs are also on the external face. We want to take one of
those edges to get to the next vertex on the external face.
On input *pPrevLink indicates which link we followed to arrive at
curVertex. On output *pPrevLink will be set to the link we follow to
get into the next vertex.
To get to the next vertex, we use the opposite link from the one used
to get into curVertex. This takes us to an edge node. The twinArc
of that edge node, carries us to an edge node in the next vertex.
At least one of the two links in that edge node will lead to a vertex
node in G, which is the next vertex. Once we arrive at the next
vertex, at least one of its links will lead back to the edge node, and
that link becomes the output value of *pPrevLink.
NOTE: This method intentionally ignores the extFace optimization
links. It is invoked when the "real" external face must be
traversed and hence when the constant time guarantee is not
needed from the extFace short-circuit that connects the
bicomp root to the first active vertices along each external
face path emanating from the bicomp root.
********************************************************************/
int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink)
{
/* Exit curVertex from whichever link was not previously used to enter it */
int arc = gp_GetArc(theGraph, curVertex, 1^(*pPrevLink));
int nextVertex = gp_GetNeighbor(theGraph, arc);
/* This if stmt assigns the new prev link that tells us which edge
record was used to enter nextVertex (so that we exit from the
opposing edge record).
However, if we are in a singleton bicomp, then both links in nextVertex
lead back to curVertex. We want the two arcs of a singleton bicomp to
act like a cycle, so we just don't change the prev link in this case.
But when nextVertex has more than one edge, we need to figure out
whether the first edge or last edge (which are the two on the external
face) was used to enter nextVertex so we can exit from the other one
as traversal of the external face continues later. */
if (gp_GetFirstArc(theGraph, nextVertex) != gp_GetLastArc(theGraph, nextVertex))
*pPrevLink = gp_GetTwinArc(theGraph, arc) == gp_GetFirstArc(theGraph, nextVertex) ? 0 : 1;
return nextVertex;
}
/****************************************************************************
_FindActiveVertices()
Descends from the root of a bicomp R along both external face paths (which
are indicated by the first and last arcs in R's adjacency list), returning
the first active vertex appearing in each direction.
****************************************************************************/
void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY)
{
int XPrevLink=1, YPrevLink=0, v=theGraph->IC.v;
*pX = _GetNeighborOnExtFace(theGraph, R, &XPrevLink);
*pY = _GetNeighborOnExtFace(theGraph, R, &YPrevLink);
// For planarity algorithms, advance past inactive vertices
// For outerplanarity algorithms, ignore the notion of inactive vertices
// since all vertices must remain on the external face.
if (!(theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR))
{
gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v);
while (INACTIVE(theGraph, *pX, v))
{
*pX = _GetNeighborOnExtFace(theGraph, *pX, &XPrevLink);
gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v);
}
gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v);
while (INACTIVE(theGraph, *pY, v))
{
*pY = _GetNeighborOnExtFace(theGraph, *pY, &YPrevLink);
gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v);
}
}
}
/****************************************************************************
_FindPertinentVertex()
Get the first vertex after x. Since x was obtained using a prevlink of 1 on r,
we use the same prevlink so we don't go back to R.
Then, we proceed around the lower path until we find a vertex W that either
has pertinent child bicomps or is directly adjacent to the current vertex v.
****************************************************************************/
int _FindPertinentVertex(graphP theGraph)
{
int W=theGraph->IC.x, WPrevLink=1;
W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink);
while (W != theGraph->IC.y)
{
if (PERTINENT(theGraph, W))
return W;
W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink);
}
return NIL;
}
/****************************************************************************
_SetVertexTypesForMarkingXYPath()
Label the vertices along the external face of the bicomp rooted at R as
'high RXW', 'low RXW', 'high RXY', 'low RXY'
****************************************************************************/
int _SetVertexTypesForMarkingXYPath(graphP theGraph)
{
int v, R, X, Y, W, Z, ZPrevLink, ZType;
// Unpack the context for efficiency of loops
v = theGraph->IC.v;
R = theGraph->IC.r;
X = theGraph->IC.x;
Y = theGraph->IC.y;
W = theGraph->IC.w;
// Ensure basic preconditions of this routine are met
if (gp_IsNotVertex(R) || gp_IsNotVertex(X) || gp_IsNotVertex(Y) || gp_IsNotVertex(W))
return NOTOK;
// Clear the type member of each vertex in the bicomp
if (_ClearVertexTypeInBicomp(theGraph, R) != OK)
return NOTOK;
// Traverse from R to W in the X direction
ZPrevLink = 1;
Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink);
ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RXW;
while (Z != W)
{
if (Z == X) ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RXW;
gp_ResetVertexObstructionType(theGraph, Z, ZType);
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
// Traverse from R to W in the Y direction
ZPrevLink = 0;
Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink);
ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RYW;
while (Z != W)
{
if (Z == Y) ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RYW;
gp_ResetVertexObstructionType(theGraph, Z, ZType);
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
return OK;
}
/****************************************************************************
_PopAndUnmarkVerticesAndEdges()
Pop all vertex/edge pairs from the top of the stack up to a terminating
vertex Z and mark as unvisited. If Z is NIL, then all vertex/edge pairs
are popped and marked as unvisited.
The stackBottom indicates where other material besides the vertex/edge
pairs may appear.
****************************************************************************/
int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom)
{
int V, e;
// Pop vertex/edge pairs until all have been popped from the stack,
// and all that's left is what was under the pairs, or until...
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
// If we pop the terminating vertex Z, then put it back and break
if (V == Z)
{
sp_Push(theGraph->theStack, V);
break;
}
// Otherwise, pop the edge part of the vertex/edge pair
sp_Pop(theGraph->theStack, e);
// Now unmark the vertex and edge (i.e. revert to "unvisited")
gp_ClearVertexVisited(theGraph, V);
gp_ClearEdgeVisited(theGraph, e);
gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
}
return OK;
}
/****************************************************************************
_MarkHighestXYPath()
An X-Y path in the bicomp rooted by R is a path attached to the external
face at points Px and Py that separates W from R such that a back edge (R, W)
cannot be embedded within the bicomp. Recall that R is a root copy of v, so
(R, W) is the representative of (v, W). Also, note that W is pertinent if
either W *or* one of its descendants in a separate bicomp has, in the input
graph, a back edge to v.
If no X-Y path separating W from R is found, then NOTOK is returned because
the proof of correctness guarantees that one exists (although this routine
can also be used to help test for the existence of an X-Y path, and NOTOK
means 'no' in that case).
The desired output is to set the 'visited' flags of the X-Y path with
highest points of attachment to the external face (i.e. the points of
attachment that are closest to R along the external face). This includes
marking both the vertices and edges along the X-Y path.
Previously, during non-planarity context initialization, the vertices along
the external face (other than R and W) have been classified as 'high RXW',
'low RXW', 'high RXY', or 'low RXY'. Once the vertices have been categorized,
we proceed with trying to set the visitation flags in the way described above.
First, we remove all edges incident to R except the two edges that join R to
the external face. The result is that R and its two remaining edges are a
'corner' in the external face but also in a single proper face whose boundary
includes the X-Y path with the highest attachment points. Thus, we simply need
to walk this proper face to find the desired X-Y path. Note, however, that the
resulting face boundary may have attached cut vertices. Any such separable
component contains a vertex neighbor of R, but the edge to R has been
temporarily removed. The algorithm removes loop of vertices and edges along
the proper face so that only a path is identified.
To walk the proper face containing R, we begin with its first arc successor,
then take the *predecessor* arc at every subsequent corner. For each vertex,
we mark as visited the vertex as well as the edge used to enter the vertex
(except for the edge used to enter the RXW vertex). We also push the visited
vertices and edges onto a stack.
As we walk the proper face, we keep track of the last vertex P_x we visited of
type RXW (high or low). Each time we encounter a vertex of type RXW (high or
low), we pop the stack and unmark all of the edges and vertices visited because
they were part of a path parallel to the external face that does not obstruct
W from reaching R within the bicomp. If we encounter vertex W, then there is
no obstructing X-Y path since we removed only edges incident to R, so we pop
the stack unmarking everything then return NOTOK as stated above. If we
encounter a vertex Z previously visited, then we pop the stack, unmarking the
vertices and edges popped, until we find the prior occurence of Z on the stack.
Otherwise, the first time we encounter a vertex P_y of type 'RYW', we stop
because the obstructing X-Y path has been marked visited and its points of
connection P_x and P_y have been found.
Once the X-Y path is identified, we restore the edges incident to R.
This method uses the stack, but it preserves any prior content.
The stack space used is no greater than 3N. The first N accounts for removing
the edges incident to R. The other 2N accounts for the fact that each
iteration of the main loop visits a vertex, pushing its index and the
location of an edge record. If a vertex is encountered that is already
on the stack, then it is not pushed again (and in fact part of the stack
is removed).
Returns TRUE if the X-Y path is found, FALSE otherwise.
In debug mode it can also return NOTOK. This is equivalent to FALSE, but
NOTOK is better for documenting the error condition in the code, and
it produces a debug message. Also, in many cases the equivalent-to-FALSE
result is an error condition for the caller, so NOTOK usually percolates up.
****************************************************************************/
int _MarkHighestXYPath(graphP theGraph)
{
int e, Z;
int R, X, Y, W;
int stackBottom1, stackBottom2;
/* Initialization */
R = theGraph->IC.r;
X = theGraph->IC.x;
Y = theGraph->IC.y;
W = theGraph->IC.w;
theGraph->IC.px = theGraph->IC.py = NIL;
/* Save the stack bottom before we start hiding internal edges, so
we will know how many edges to restore */
stackBottom1 = sp_GetCurrentSize(theGraph->theStack);
/* Remove the internal edges incident to vertex R */
if (_HideInternalEdges(theGraph, R) != OK)
return NOTOK;
/* Now we're going to use the stack to collect the vertices of potential
* X-Y paths, so we need to store where the hidden internal edges are
* located because we must, at times, pop the collected vertices if
* the path being collected doesn't work out. */
stackBottom2 = sp_GetCurrentSize(theGraph->theStack);
/* Walk the proper face containing R to find and mark the highest
X-Y path. Note that if W is encountered, then there is no
intervening X-Y path, so we would return FALSE in that case. */
Z = R;
// This setting of e is the arc equivalent of prevLink=1
// As loop progresses, e indicates the arc used to enter Z, not the exit arc
e = gp_GetLastArc(theGraph, R);
while (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_HIGH_RYW &&
gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_LOW_RYW)
{
/* Advance e and Z along the proper face containing R */
e = gp_GetPrevArcCircular(theGraph, e);
Z = gp_GetNeighbor(theGraph, e);
e = gp_GetTwinArc(theGraph, e);
/* If Z is already visited, then pop everything since the last time
we visited Z because its all part of a separable component. */
if (gp_GetVertexVisited(theGraph, Z))
{
if (_PopAndUnmarkVerticesAndEdges(theGraph, Z, stackBottom2) != OK)
return NOTOK;
}
/* If we have not visited this vertex before... */
else
{
/* If we find W, then there is no X-Y path. Never happens
for Kuratowski subgraph isolator, but this routine is
also used to test for certain X-Y paths.
So, we clean up and bail out in that case. */
if (Z == W)
{
if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK)
return NOTOK;
break;
}
/* If we found another vertex along the RXW path, then blow off
all the vertices we visited so far because they're not part of
the obstructing path */
if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW ||
gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW)
{
theGraph->IC.px = Z;
if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK)
return NOTOK;
}
/* Push the current vertex onto the stack of vertices visited
since the last RXW vertex was encountered */
sp_Push(theGraph->theStack, e);
sp_Push(theGraph->theStack, Z);
/* Mark the vertex Z as visited as well as its edge of entry
(except the entry edge for P_x).*/
gp_SetVertexVisited(theGraph, Z);
if (Z != theGraph->IC.px)
{
gp_SetEdgeVisited(theGraph, e);
gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
}
/* If we found an RYW vertex, then we have successfully finished
identifying the highest X-Y path, so we record the point of
attachment and break the loop. */
if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW ||
gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW)
{
theGraph->IC.py = Z;
break;
}
}
}
/* Remove any remaining vertex-edge pairs on the top of the stack, then
Restore the internal edges incident to R that were previously removed. */
sp_SetCurrentSize(theGraph->theStack, stackBottom2);
if (_RestoreInternalEdges(theGraph, stackBottom1) != OK)
return NOTOK;
/* Return the result */
return gp_IsVertex(theGraph->IC.py) ? TRUE : FALSE;
}
/****************************************************************************
_MarkZtoRPath()
This function assumes that _MarkHighestXYPath() has already been called,
which marked as visited the vertices and edges along the X-Y path.
We begin at the point of attachment P_x, take the last arc and traverse
the predecessor arcs until we find one marked visited, which leads to the
first internal vertex along the X-Y path. We begin with this vertex
(and its edge of entry), and we run until we find P_y. For each internal
vertex Z and its edge of entry ZPrevArc, we take the predecessor edge record
of ZPrevArc. This is called ZNextArc. If ZNextArc is marked visited
then it is along the X-Y path, so we use it to exit Z and go to the next
vertex on the X-Y path.
If ZNextArc is not visited, then when _MarkHighestXYPath() ran, it exited
Z from ZNextArc, then eventually reentered Z. In other words, Z became a
cut vertex when we removed the internal edges incident to R. Thus, ZNextArc
indicates the first edge in an internal path to R.
When we find an unvisited ZNextArc, we stop running the X-Y path and instead
begin marking the Z to R path. We move to successive vertices using a
twin arc then its predecessor arc in the adjacency list, only this time
we have not removed the internal edges incident to R, so this technique does
eventually lead us all the way to R.
If we do not find an unvisited ZNextArc for any vertex Z on the X-Y path and
inside the bicomp, then there is no Z to R path, so we return.
****************************************************************************/
int _MarkZtoRPath(graphP theGraph)
{
int ZPrevArc, ZNextArc, Z, R, Px, Py;
/* Initialize */
R = theGraph->IC.r;
Px = theGraph->IC.px;
Py = theGraph->IC.py;
theGraph->IC.z = NIL;
/* Begin at Px and search its adjacency list for the edge leading to
the first internal vertex of the X-Y path. */
Z = Px;
ZNextArc = gp_GetLastArc(theGraph, Z);
while (ZNextArc != gp_GetFirstArc(theGraph, Z))
{
if (gp_GetEdgeVisited(theGraph, ZNextArc)) break;
ZNextArc = gp_GetPrevArc(theGraph, ZNextArc);
}
if (!gp_GetEdgeVisited(theGraph, ZNextArc))
return NOTOK;
/* For each internal vertex Z, determine whether it has a path to root. */
while (gp_GetEdgeVisited(theGraph, ZNextArc))
{
ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc);
ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc);
}
ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc);
Z = gp_GetNeighbor(theGraph, ZPrevArc);
/* If there is no Z to R path, return */
if (Z == Py) return OK;
/* Otherwise, store Z in the isolation context */
theGraph->IC.z = Z;
/* Walk the proper face starting with (Z, ZNextArc) until we reach R, marking
the vertices and edges encountered along the way, then Return OK. */
while (Z != R)
{
/* If we ever encounter a non-internal vertex (other than the root R),
then corruption has occured, so we return NOTOK */
if (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_UNKNOWN)
return NOTOK;
/* Go to the next vertex indicated by ZNextArc */
Z = gp_GetNeighbor(theGraph, ZNextArc);
/* Mark the next vertex and the edge leading to it as visited. */
gp_SetEdgeVisited(theGraph, ZNextArc);
gp_SetEdgeVisited(theGraph, ZPrevArc);
gp_SetVertexVisited(theGraph, Z);
/* Go to the next edge in the proper face */
ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc);
ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc);
}
/* Found Z to R path, so indicate as much to caller */
return OK;
}
/****************************************************************************
_FindFuturePertinenceBelowXYPath()
Get a future pertinent vertex along the lower external face path between
the points of attachment P_x and P_y of a 'low' X-Y Path.
NOTE: By the time this function is called, Px and Py have already been found
to be at or below X and Y.
****************************************************************************/
int _FindFuturePertinenceBelowXYPath(graphP theGraph)
{
int Z=theGraph->IC.px, ZPrevLink=1,
Py=theGraph->IC.py, v=theGraph->IC.v;
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
while (Z != Py)
{
gp_UpdateVertexFuturePertinentChild(theGraph, Z, v);
if (FUTUREPERTINENT(theGraph, Z, v))
return Z;
Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);
}
return NIL;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphOuterplanarObstruction.c 0000664 0000000 0000000 00000016107 12716155161 0027361 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "graph.h"
/* Imported functions */
extern void _ClearVisitedFlags(graphP);
extern int _JoinBicomps(graphP theGraph);
extern int _InitializeNonplanarityContext(graphP theGraph, int v, int R);
extern int _MarkHighestXYPath(graphP theGraph);
//extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant);
extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);
extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant);
extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
/* Private function declarations (exported to system) */
int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R);
int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R);
int _IsolateOuterplanarityObstructionA(graphP theGraph);
int _IsolateOuterplanarityObstructionB(graphP theGraph);
int _IsolateOuterplanarityObstructionE(graphP theGraph);
/****************************************************************************
_ChooseTypeOfNonOuterplanarityMinor()
A constant time implementation is easily feasible but only constant amortized
time is needed for the outerplanarity obstruction isolation, which also
benefits from having the bicomp rooted by R oriented.
If an extension algorithm requires constant actual time, then this function
should not be used and instead the minor should be decided without orienting
the bicomp.
****************************************************************************/
int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R)
{
int X, Y, W;
// Create the initial non-outerplanarity obstruction isolator state.
if (_InitializeNonplanarityContext(theGraph, v, R) != OK)
return NOTOK;
R = theGraph->IC.r;
X = theGraph->IC.x;
Y = theGraph->IC.y;
W = theGraph->IC.w;
// If the root copy is not a root copy of the current vertex v,
// then the Walkdown terminated on a descendant bicomp, which is Minor A.
if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v)
{
theGraph->IC.minorType |= MINORTYPE_A;
return OK;
}
// If W has a pertinent child bicomp, then we've found Minor B.
// Notice this is different from planarity, in which minor B is indicated
// only if the pertinent child bicomp is also future pertinent.
if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W)))
{
theGraph->IC.minorType |= MINORTYPE_B;
return OK;
}
// The only other result is minor E (we will search for the X-Y path later)
theGraph->IC.minorType |= MINORTYPE_E;
return OK;
}
/****************************************************************************
_IsolateOuterplanarObstruction()
****************************************************************************/
int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R)
{
int RetVal;
/* A subgraph homeomorphic to K_{2,3} or K_4 will be isolated by using the visited
flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we
subsequently set visited on all edges and vertices in the homeomorph. */
_ClearVisitedFlags(theGraph);
/* Next we determineg which of the non-outerplanarity Minors was encountered
and the principal bicomp on which the isolator will focus attention. */
if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK)
return NOTOK;
/* Find the path connecting the pertinent vertex w with the current vertex v */
if (theGraph->IC.minorType & MINORTYPE_B)
{
isolatorContextP IC = &theGraph->IC;
int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w);
if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE)
return NOTOK;
}
else
{
isolatorContextP IC = &theGraph->IC;
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
return NOTOK;
}
/* For minor E, we need to find and mark an X-Y path */
if (theGraph->IC.minorType & MINORTYPE_E)
{
if (_MarkHighestXYPath(theGraph) != TRUE)
return NOTOK;
}
/* Call the appropriate isolator */
if (theGraph->IC.minorType & MINORTYPE_A)
RetVal = _IsolateOuterplanarityObstructionA(theGraph);
else if (theGraph->IC.minorType & MINORTYPE_B)
RetVal = _IsolateOuterplanarityObstructionB(theGraph);
else if (theGraph->IC.minorType & MINORTYPE_E)
RetVal = _IsolateOuterplanarityObstructionE(theGraph);
else
RetVal = NOTOK;
/* Delete the unmarked edges and vertices, and return */
if (RetVal == OK)
RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph);
return RetVal;
}
/****************************************************************************
_IsolateOuterplanarityObstructionA(): Isolate a K2,3 homeomorph
****************************************************************************/
int _IsolateOuterplanarityObstructionA(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->v, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateOuterplanarityObstructionB(): Isolate a K2,3 homeomorph
****************************************************************************/
int _IsolateOuterplanarityObstructionB(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK)
return NOTOK;
return OK;
}
/****************************************************************************
_IsolateOuterplanarityObstructionE(): Isolate a K4 homeomorph
****************************************************************************/
int _IsolateOuterplanarityObstructionE(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK ||
theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK ||
_JoinBicomps(theGraph) != OK ||
_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK)
return NOTOK;
return OK;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphStructures.h 0000664 0000000 0000000 00000107125 12716155161 0025022 0 ustar 00root root 0000000 0000000 #ifndef GRAPHSTRUCTURE_H
#define GRAPHSTRUCTURE_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "appconst.h"
#include "listcoll.h"
#include "stack.h"
#include "graphFunctionTable.h"
#include "graphExtensions.private.h"
#ifdef __cplusplus
extern "C" {
#endif
// A return value to indicate success prior to completely processing a graph, whereas
// OK signifies EMBEDDABLE (no unreducible obstructions) and NOTOK signifies an exception.
#define NONEMBEDDABLE -1
// The initial setting for the edge storage capacity expressed as a constant factor of N,
// which is the number of vertices in the graph. By default, array E is allocated enough
// space to contain 3N edges, which is 6N arcs (half edges), but this initial setting
// can be overridden using gp_EnsureArcCapacity(), which is especially efficient if done
// before calling gp_InitGraph() or gp_Read().
#define DEFAULT_EDGE_LIMIT 3
/********************************************************************
Edge Record Definition
An edge is defined by a pair of edge records, or arcs, allocated in
array E of a graph. An edge record represents the edge in the
adjacency list of each vertex to which the edge is incident.
link[2]: the next and previous edge records (arcs) in the adjacency
list that contains this edge record.
v: The vertex neighbor of the vertex whose adjacency list contains
this edge record (an index into array V).
flags: Bits 0-15 reserved for library; bits 16 and higher for apps
Bit 0: Visited
Bit 1: DFS type has been set, versus not set
Bit 2: DFS tree edge, versus cycle edge (co-tree edge, etc.)
Bit 3: DFS arc to descendant, versus arc to ancestor
Bit 4: Inverted (same as marking an edge with a "sign" of -1)
Bit 5: Arc is directed into the containing vertex only
Bit 6: Arc is directed from the containing vertex only
********************************************************************/
typedef struct
{
int link[2];
int neighbor;
unsigned flags;
} edgeRec;
typedef edgeRec * edgeRecP;
#if NIL == 0
#define gp_IsArc(e) (e)
#define gp_IsNotArc(e) (!(e))
#define gp_GetFirstEdge(theGraph) (2)
#elif NIL == -1
#define gp_IsArc(e) ((e) != NIL)
#define gp_IsNotArc(e) ((e) == NIL)
#define gp_GetFirstEdge(theGraph) (0)
#else
#error NIL must be 0 or -1
#endif
#define gp_EdgeInUse(theGraph, e) (gp_IsVertex(gp_GetNeighbor(theGraph, e)))
#define gp_EdgeNotInUse(theGraph, e) (gp_IsNotVertex(gp_GetNeighbor(theGraph, e)))
#define gp_EdgeIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (theGraph)->arcCapacity)
#define gp_EdgeInUseIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (((theGraph)->M + sp_GetCurrentSize((theGraph)->edgeHoles)) << 1))
// An edge is represented by two consecutive edge records (arcs) in the edge array E.
// If an even number, xor 1 will add one; if an odd number, xor 1 will subtract 1
#define gp_GetTwinArc(theGraph, Arc) ((Arc) ^ 1)
// Access to adjacency list pointers
#define gp_GetNextArc(theGraph, e) (theGraph->E[e].link[0])
#define gp_GetPrevArc(theGraph, e) (theGraph->E[e].link[1])
#define gp_GetAdjacentArc(theGraph, e, theLink) (theGraph->E[e].link[theLink])
#define gp_SetNextArc(theGraph, e, newNextArc) (theGraph->E[e].link[0] = newNextArc)
#define gp_SetPrevArc(theGraph, e, newPrevArc) (theGraph->E[e].link[1] = newPrevArc)
#define gp_SetAdjacentArc(theGraph, e, theLink, newArc) (theGraph->E[e].link[theLink] = newArc)
// Access to vertex 'neighbor' member indicated by arc
#define gp_GetNeighbor(theGraph, e) (theGraph->E[e].neighbor)
#define gp_SetNeighbor(theGraph, e, v) (theGraph->E[e].neighbor = v)
// Initializer for edge flags
#define gp_InitEdgeFlags(theGraph, e) (theGraph->E[e].flags = 0)
// Definitions of and access to edge flags
#define EDGE_VISITED_MASK 1
#define gp_GetEdgeVisited(theGraph, e) (theGraph->E[e].flags&EDGE_VISITED_MASK)
#define gp_ClearEdgeVisited(theGraph, e) (theGraph->E[e].flags &= ~EDGE_VISITED_MASK)
#define gp_SetEdgeVisited(theGraph, e) (theGraph->E[e].flags |= EDGE_VISITED_MASK)
// The edge type is defined by bits 1-3, 2+4+8=14
#define EDGE_TYPE_MASK 14
// Call gp_GetEdgeType(), then compare to one of these four possibilities
// EDGE_TYPE_CHILD - edge record is an arc to a DFS child
// EDGE_TYPE_FORWARD - edge record is an arc to a DFS descendant, not a DFS child
// EDGE_TYPE_PARENT - edge record is an arc to the DFS parent
// EDGE_TYPE_BACK - edge record is an arc to a DFS ancestor, not the DFS parent
#define EDGE_TYPE_CHILD 14
#define EDGE_TYPE_FORWARD 10
#define EDGE_TYPE_PARENT 6
#define EDGE_TYPE_BACK 2
// EDGE_TYPE_NOTDEFINED - the edge record type has not been defined
// EDGE_TYPE_RANDOMTREE - edge record is part of a randomly generated tree
#define EDGE_TYPE_NOTDEFINED 0
#define EDGE_TYPE_RANDOMTREE 4
#define gp_GetEdgeType(theGraph, e) (theGraph->E[e].flags&EDGE_TYPE_MASK)
#define gp_ClearEdgeType(theGraph, e) (theGraph->E[e].flags &= ~EDGE_TYPE_MASK)
#define gp_SetEdgeType(theGraph, e, type) (theGraph->E[e].flags |= type)
#define gp_ResetEdgeType(theGraph, e, type) \
(theGraph->E[e].flags = (theGraph->E[e].flags & ~EDGE_TYPE_MASK) | type)
#define EDGEFLAG_INVERTED_MASK 16
#define gp_GetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_INVERTED_MASK)
#define gp_SetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags |= EDGEFLAG_INVERTED_MASK)
#define gp_ClearEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags &= (~EDGEFLAG_INVERTED_MASK))
#define gp_XorEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags ^= EDGEFLAG_INVERTED_MASK)
#define EDGEFLAG_DIRECTION_INONLY 32
#define EDGEFLAG_DIRECTION_OUTONLY 64
#define EDGEFLAG_DIRECTION_MASK 96
// Returns the direction, if any, of the edge record
#define gp_GetDirection(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_DIRECTION_MASK)
//A direction of 0 clears directedness. Otherwise, edge record e is set
//to edgeFlag_Direction and e's twin arc is set to the opposing setting.
#define gp_SetDirection(theGraph, e, edgeFlag_Direction) \
{ \
if (edgeFlag_Direction == EDGEFLAG_DIRECTION_INONLY) \
{ \
theGraph->E[e].flags |= EDGEFLAG_DIRECTION_INONLY; \
theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_OUTONLY; \
} \
else if (edgeFlag_Direction == EDGEFLAG_DIRECTION_OUTONLY) \
{ \
theGraph->E[e].flags |= EDGEFLAG_DIRECTION_OUTONLY; \
theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_INONLY; \
} \
else \
{ \
theGraph->E[e].flags &= ~(EDGEFLAG_DIRECTION_INONLY|EDGEFLAG_DIRECTION_OUTONLY); \
theGraph->E[gp_GetTwinArc(theGraph, e)].flags &= ~EDGEFLAG_DIRECTION_MASK; \
} \
}
#define gp_CopyEdgeRec(dstGraph, edst, srcGraph, esrc) (dstGraph->E[edst] = srcGraph->E[esrc])
/********************************************************************
Vertex Record Definition
This record definition provides the data members needed for the
core structural information for both vertices and virtual vertices.
Vertices are also equipped with additional information provided by
the vertexInfo structure.
The vertices of a graph are stored in the first N locations of array V.
Virtual vertices are secondary vertices used to help represent the
main vertices in substructural components of a graph (e.g. biconnected
components).
link[2]: the first and last edge records (arcs) in the adjacency list
of the vertex.
index: In vertices, stores either the depth first index of a vertex or
the original array index of the vertex if the vertices of the
graph are sorted by DFI.
In virtual vertices, the index may be used to indicate the vertex
that the virtual vertex represents, unless an algorithm has some
other way of making the association (for example, the planarity
algorithms rely on biconnected components and therefore place
virtual vertices of a vertex at positions corresponding to the
DFS children of the vertex).
flags: Bits 0-15 reserved for library; bits 16 and higher for apps
Bit 0: visited, for vertices and virtual vertices
Use in lieu of TYPE_VERTEX_VISITED in K4 algorithm
Bit 1: Obstruction type VERTEX_TYPE_SET (versus not set, i.e. VERTEX_TYPE_UNKNOWN)
Bit 2: Obstruction type qualifier RYW (set) versus RXW (clear)
Bit 3: Obstruction type qualifier high (set) versus low (clear)
********************************************************************/
typedef struct
{
int link[2];
int index;
unsigned flags;
} vertexRec;
typedef vertexRec * vertexRecP;
// Accessors for vertex adjacency list links
#define gp_GetFirstArc(theGraph, v) (theGraph->V[v].link[0])
#define gp_GetLastArc(theGraph, v) (theGraph->V[v].link[1])
#define gp_GetArc(theGraph, v, theLink) (theGraph->V[v].link[theLink])
#define gp_SetFirstArc(theGraph, v, newFirstArc) (theGraph->V[v].link[0] = newFirstArc)
#define gp_SetLastArc(theGraph, v, newLastArc) (theGraph->V[v].link[1] = newLastArc)
#define gp_SetArc(theGraph, v, theLink, newArc) (theGraph->V[v].link[theLink] = newArc)
// Vertex conversions and iteration
#if NIL == 0
#define gp_IsVertex(v) (v)
#define gp_IsNotVertex(v) (!(v))
#define gp_GetFirstVertex(theGraph) (1)
#define gp_GetLastVertex(theGraph) ((theGraph)->N)
#define gp_VertexInRange(theGraph, v) ((v) <= (theGraph)->N)
#define gp_VertexInRangeDescending(theGraph, v) (v)
#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N)
#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N)
#define gp_IsVirtualVertex(theGraph, v) ((v) > theGraph->N)
#define gp_IsNotVirtualVertex(theGraph, v) ((v) <= theGraph->N)
#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex)))
#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex)))
#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N + 1)
#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV)
#define gp_VirtualVertexInRange(theGraph, v) ((v) <= theGraph->N + theGraph->NV)
#elif NIL == -1
#define gp_IsVertex(v) ((v) != NIL)
#define gp_IsNotVertex(v) ((v) == NIL)
#define gp_GetFirstVertex(theGraph) (0)
#define gp_GetLastVertex(theGraph) ((theGraph)->N - 1)
#define gp_VertexInRange(theGraph, v) ((v) < (theGraph)->N)
#define gp_VertexInRangeDescending(theGraph, v) ((v) >= 0)
#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N)
#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N)
#define gp_IsVirtualVertex(theGraph, v) ((v) >= theGraph->N)
#define gp_IsNotVirtualVertex(theGraph, v) ((v) < theGraph->N)
#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex)))
#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex)))
#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N)
#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV - 1)
#define gp_VirtualVertexInRange(theGraph, v) ((v) < theGraph->N + theGraph->NV)
#else
#error NIL must be 0 or -1
#endif
#define gp_GetRootFromDFSChild(theGraph, c) ((c) + theGraph->N)
#define gp_GetDFSChildFromRoot(theGraph, R) ((R) - theGraph->N)
#define gp_GetPrimaryVertexFromRoot(theGraph, R) gp_GetVertexParent(theGraph, gp_GetDFSChildFromRoot(theGraph, R))
#define gp_IsSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild)))
#define gp_IsNotSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexNotInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild)))
#define gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(gp_GetVertexParent(theGraph, v))
#define gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(gp_GetVertexParent(theGraph, v))
// Accessors for vertex index
#define gp_GetVertexIndex(theGraph, v) (theGraph->V[v].index)
#define gp_SetVertexIndex(theGraph, v, theIndex) (theGraph->V[v].index = theIndex)
// Initializer for vertex flags
#define gp_InitVertexFlags(theGraph, v) (theGraph->V[v].flags = 0)
// Definitions and accessors for vertex flags
#define VERTEX_VISITED_MASK 1
#define gp_GetVertexVisited(theGraph, v) (theGraph->V[v].flags&VERTEX_VISITED_MASK)
#define gp_ClearVertexVisited(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_VISITED_MASK)
#define gp_SetVertexVisited(theGraph, v) (theGraph->V[v].flags |= VERTEX_VISITED_MASK)
// The obstruction type is defined by bits 1-3, 2+4+8=14
// Bit 1 - 2 if type set, 0 if not
// Bit 2 - 4 if Y side, 0 if X side
// Bit 3 - 8 if high, 0 if low
#define VERTEX_OBSTRUCTIONTYPE_MASK 14
// Call gp_GetVertexObstructionType, then compare to one of these four possibilities
// VERTEX_OBSTRUCTIONTYPE_HIGH_RXW - On the external face path between vertices R and X
// VERTEX_OBSTRUCTIONTYPE_LOW_RXW - X or on the external face path between vertices X and W
// VERTEX_OBSTRUCTIONTYPE_HIGH_RYW - On the external face path between vertices R and Y
// VERTEX_OBSTRUCTIONTYPE_LOW_RYW - Y or on the external face path between vertices Y and W
// VERTEX_OBSTRUCTIONTYPE_UNKNOWN - corresponds to all three bits off
#define VERTEX_OBSTRUCTIONTYPE_HIGH_RXW 10
#define VERTEX_OBSTRUCTIONTYPE_LOW_RXW 2
#define VERTEX_OBSTRUCTIONTYPE_HIGH_RYW 14
#define VERTEX_OBSTRUCTIONTYPE_LOW_RYW 6
#define VERTEX_OBSTRUCTIONTYPE_UNKNOWN 0
#define VERTEX_OBSTRUCTIONTYPE_MARKED 2
#define VERTEX_OBSTRUCTIONTYPE_UNMARKED 0
#define gp_GetVertexObstructionType(theGraph, v) (theGraph->V[v].flags&VERTEX_OBSTRUCTIONTYPE_MASK)
#define gp_ClearVertexObstructionType(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_OBSTRUCTIONTYPE_MASK)
#define gp_SetVertexObstructionType(theGraph, v, type) (theGraph->V[v].flags |= type)
#define gp_ResetVertexObstructionType(theGraph, v, type) \
(theGraph->V[v].flags = (theGraph->V[v].flags & ~VERTEX_OBSTRUCTIONTYPE_MASK) | type)
#define gp_CopyVertexRec(dstGraph, vdst, srcGraph, vsrc) (dstGraph->V[vdst] = srcGraph->V[vsrc])
#define gp_SwapVertexRec(dstGraph, vdst, srcGraph, vsrc) \
{ \
vertexRec tempV = dstGraph->V[vdst]; \
dstGraph->V[vdst] = srcGraph->V[vsrc]; \
srcGraph->V[vsrc] = tempV; \
}
/********************************************************************
This structure defines a pair of links used by each vertex and virtual vertex
to create "short circuit" paths that eliminate unimportant vertices from
the external face, enabling more efficient traversal of the external face.
It is also possible to embed the "short circuit" edges, but this approach
creates a better separation of concerns, imparts greater clarity, and
removes exceptionalities for handling additional fake "short circuit" edges.
vertex[2]: The two adjacent vertices along the external face, possibly
short-circuiting paths of inactive vertices.
*/
typedef struct
{
int vertex[2];
} extFaceLinkRec;
typedef extFaceLinkRec * extFaceLinkRecP;
#define gp_GetExtFaceVertex(theGraph, v, link) (theGraph->extFace[v].vertex[link])
#define gp_SetExtFaceVertex(theGraph, v, link, theVertex) (theGraph->extFace[v].vertex[link] = theVertex)
/********************************************************************
Vertex Info Structure Definition.
This structure equips the primary (non-virtual) vertices with additional
information needed for lowpoint and planarity-related algorithms.
parent: The DFI of the DFS tree parent of this vertex
leastAncestor: min(DFI of neighbors connected by backedge)
lowpoint: min(leastAncestor, min(lowpoint of DFS Children))
visitedInfo: enables algorithms to manage vertex visitation with more than
just a flag. For example, the planarity test flags visitation
as a step number that implicitly resets on each step, whereas
part of the planar drawing method signifies a first visitation
by storing the index of the first edge used to reach a vertex
pertinentEdge: Used by the planarity method; during Walkup, each vertex
that is directly adjacent via a back edge to the vertex v
currently being embedded will have the forward edge's index
stored in this field. During Walkdown, each vertex for which
this field is set will cause a back edge to be embedded.
Implicitly resets at each vertex step of the planarity method
pertinentRootsList: used by Walkup to store a list of child bicomp roots of
a vertex descendant of the current vertex that are pertinent
and must be merged by the Walkdown in order to embed the cycle
edges of the current vertex. Future pertinent child bicomp roots
are placed at the end of the list to ensure bicomps that are
only pertinent are processed first.
futurePertinentChild: indicates a DFS child with a lowpoint less than the
current vertex v. This member is initialized to the start of
the sortedDFSChildList and is advanced in a relaxed manner as
needed until one with a lowpoint less than v is found or until
there are no more children.
sortedDFSChildList: at the start of embedding, the list of DFS children of
this vertex is calculated in ascending order by DFI (sorted in
linear time). The list is used during Walkdown processing of
a vertex to process all of its children. It is also used in
future pertinence management when processing the ancestors of
the vertex. When a child C is merged into the same bicomp as
the vertex, it is removed from the list.
fwdArcList: at the start of embedding, the "back" edges from a vertex to
its DFS *descendants* (i.e. the forward arcs of the back edges)
are separated from the main adjacency list and placed in a
circular list until they are embedded. The list is sorted in
ascending DFI order of the descendants (in linear time).
This member indicates a node in that list.
*/
typedef struct
{
int parent, leastAncestor, lowpoint;
int visitedInfo;
int pertinentEdge,
pertinentRoots,
futurePertinentChild,
sortedDFSChildList,
fwdArcList;
} vertexInfo;
typedef vertexInfo * vertexInfoP;
#define gp_GetVertexVisitedInfo(theGraph, v) (theGraph->VI[v].visitedInfo)
#define gp_SetVertexVisitedInfo(theGraph, v, theVisitedInfo) (theGraph->VI[v].visitedInfo = theVisitedInfo)
#define gp_GetVertexParent(theGraph, v) (theGraph->VI[v].parent)
#define gp_SetVertexParent(theGraph, v, theParent) (theGraph->VI[v].parent = theParent)
#define gp_GetVertexLeastAncestor(theGraph, v) (theGraph->VI[v].leastAncestor)
#define gp_SetVertexLeastAncestor(theGraph, v, theLeastAncestor) (theGraph->VI[v].leastAncestor = theLeastAncestor)
#define gp_GetVertexLowpoint(theGraph, v) (theGraph->VI[v].lowpoint)
#define gp_SetVertexLowpoint(theGraph, v, theLowpoint) (theGraph->VI[v].lowpoint = theLowpoint)
#define gp_GetVertexPertinentEdge(theGraph, v) (theGraph->VI[v].pertinentEdge)
#define gp_SetVertexPertinentEdge(theGraph, v, e) (theGraph->VI[v].pertinentEdge = e)
#define gp_GetVertexPertinentRootsList(theGraph, v) (theGraph->VI[v].pertinentRoots)
#define gp_SetVertexPertinentRootsList(theGraph, v, pertinentRootsHead) (theGraph->VI[v].pertinentRoots = pertinentRootsHead)
#define gp_GetVertexFirstPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, theGraph->VI[v].pertinentRoots)
#define gp_GetVertexFirstPertinentRootChild(theGraph, v) (theGraph->VI[v].pertinentRoots)
#define gp_GetVertexLastPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL))
#define gp_GetVertexLastPertinentRootChild(theGraph, v) LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL)
#define gp_DeleteVertexPertinentRoot(theGraph, v, R) \
gp_SetVertexPertinentRootsList(theGraph, v, \
LCDelete(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R)))
#define gp_PrependVertexPertinentRoot(theGraph, v, R) \
gp_SetVertexPertinentRootsList(theGraph, v, \
LCPrepend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R)))
#define gp_AppendVertexPertinentRoot(theGraph, v, R) \
gp_SetVertexPertinentRootsList(theGraph, v, \
LCAppend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R)))
#define gp_GetVertexFuturePertinentChild(theGraph, v) (theGraph->VI[v].futurePertinentChild)
#define gp_SetVertexFuturePertinentChild(theGraph, v, theFuturePertinentChild) (theGraph->VI[v].futurePertinentChild = theFuturePertinentChild)
// Used to advance futurePertinentChild of w to the next separated DFS child with a lowpoint less than v
// Once futurePertinentChild advances past a child, no future planarity operation could make that child
// relevant to future pertinence
#define gp_UpdateVertexFuturePertinentChild(theGraph, w, v) \
while (gp_IsVertex(theGraph->VI[w].futurePertinentChild)) \
{ \
/* Skip children that 1) aren't future pertinent, 2) have been merged into the bicomp with w */ \
if (gp_GetVertexLowpoint(theGraph, theGraph->VI[w].futurePertinentChild) >= v || \
gp_IsNotSeparatedDFSChild(theGraph, theGraph->VI[w].futurePertinentChild)) \
{ \
theGraph->VI[w].futurePertinentChild = \
gp_GetVertexNextDFSChild(theGraph, w, gp_GetVertexFuturePertinentChild(theGraph, w)); \
} \
else break; \
}
#define gp_GetVertexSortedDFSChildList(theGraph, v) (theGraph->VI[v].sortedDFSChildList)
#define gp_SetVertexSortedDFSChildList(theGraph, v, theSortedDFSChildList) (theGraph->VI[v].sortedDFSChildList = theSortedDFSChildList)
#define gp_GetVertexNextDFSChild(theGraph, v, c) LCGetNext(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c)
#define gp_AppendDFSChild(theGraph, v, c) \
LCAppend(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c)
#define gp_GetVertexFwdArcList(theGraph, v) (theGraph->VI[v].fwdArcList)
#define gp_SetVertexFwdArcList(theGraph, v, theFwdArcList) (theGraph->VI[v].fwdArcList = theFwdArcList)
#define gp_CopyVertexInfo(dstGraph, dstI, srcGraph, srcI) (dstGraph->VI[dstI] = srcGraph->VI[srcI])
#define gp_SwapVertexInfo(dstGraph, dstPos, srcGraph, srcPos) \
{ \
vertexInfo tempVI = dstGraph->VI[dstPos]; \
dstGraph->VI[dstPos] = srcGraph->VI[srcPos]; \
srcGraph->VI[srcPos] = tempVI; \
}
/********************************************************************
Variables needed in embedding by Kuratowski subgraph isolator:
minorType: the type of planarity obstruction found.
v: the current vertex being processed
r: the root of the bicomp on which the Walkdown failed
x,y: stopping vertices on bicomp rooted by r
w: pertinent vertex on ext. face path below x and y
px, py: attachment points of x-y path,
z: Unused except in minors D and E (not needed in A, B, C).
ux,dx: endpoints of unembedded edge that helps connext x with
ancestor of v
uy,dy: endpoints of unembedded edge that helps connext y with
ancestor of v
dw: descendant endpoint in unembedded edge to v
uz,dz: endpoints of unembedded edge that helps connext z with
ancestor of v (for minors B and E, not A, C, D).
*/
typedef struct
{
int minorType;
int v, r, x, y, w, px, py, z;
int ux, dx, uy, dy, dw, uz, dz;
} isolatorContext;
typedef isolatorContext * isolatorContextP;
#define MINORTYPE_A 1
#define MINORTYPE_B 2
#define MINORTYPE_C 4
#define MINORTYPE_D 8
#define MINORTYPE_E 16
#define MINORTYPE_E1 32
#define MINORTYPE_E2 64
#define MINORTYPE_E3 128
#define MINORTYPE_E4 256
#define MINORTYPE_E5 512
#define MINORTYPE_E6 1024
#define MINORTYPE_E7 2048
/********************************************************************
Graph structure definition
V : Array of vertex records (allocated size N + NV)
VI: Array of additional vertexInfo structures (allocated size N)
N : Number of primary vertices (the "order" of the graph)
NV: Number of virtual vertices (currently always equal to N)
E : Array of edge records (edge records come in pairs and represent half edges, or arcs)
M: Number of edges (the "size" of the graph)
arcCapacity: the maximum number of edge records allowed in E (the size of E)
edgeHoles: free locations in E where edges have been deleted
theStack: Used by various graph routines needing a stack
internalFlags: Additional state information about the graph
embedFlags: controls type of embedding (e.g. planar)
IC: contains additional useful variables for Kuratowski subgraph isolation.
BicompRootLists: storage space for pertinent bicomp root lists that develop
during embedding
sortedDFSChildLists: storage for the sorted DFS child lists of each vertex
extFace: Array of (N + NV) external face short circuit records
extensions: a list of extension data structures
functions: a table of function pointers that can be overloaded to provide
extension behaviors to the graph
*/
typedef struct
{
vertexRecP V;
vertexInfoP VI;
int N, NV;
edgeRecP E;
int M, arcCapacity;
stackP edgeHoles;
stackP theStack;
int internalFlags, embedFlags;
isolatorContext IC;
listCollectionP BicompRootLists, sortedDFSChildLists;
extFaceLinkRecP extFace;
graphExtensionP extensions;
graphFunctionTable functions;
} baseGraphStructure;
typedef baseGraphStructure * graphP;
/* Flags for graph:
FLAGS_DFSNUMBERED is set if DFSNumber() has succeeded for the graph
FLAGS_SORTEDBYDFI records whether the graph is in original vertex
order or sorted by depth first index. Successive calls to
SortVertices() toggle this bit.
FLAGS_OBSTRUCTIONFOUND is set by gp_Embed() if an embedding obstruction
was isolated in the graph returned. It is cleared by gp_Embed()
if an obstruction was not found. The flag is used by
gp_TestEmbedResultIntegrity() to decide what integrity tests to run.
FLAGS_ZEROBASEDIO is typically set by gp_Read() to indicate that the
adjacency list representation began with index 0.
*/
#define FLAGS_DFSNUMBERED 1
#define FLAGS_SORTEDBYDFI 2
#define FLAGS_OBSTRUCTIONFOUND 4
#define FLAGS_ZEROBASEDIO 8
/********************************************************************
More link structure accessors/manipulators
********************************************************************/
// Definitions that enable getting the next or previous arc
// as if the adjacency list were circular, i.e. that the
// first arc and last arc were linked
#define gp_GetNextArcCircular(theGraph, e) \
(gp_IsArc(gp_GetNextArc(theGraph, e)) ? \
gp_GetNextArc(theGraph, e) : \
gp_GetFirstArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor))
#define gp_GetPrevArcCircular(theGraph, e) \
(gp_IsArc(gp_GetPrevArc(theGraph, e)) ? \
gp_GetPrevArc(theGraph, e) : \
gp_GetLastArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor))
// Definitions that make the cross-link binding between a vertex and an arc
// The old first or last arc should be bound to this arc by separate calls,
// e.g. see gp_AttachFirstArc() and gp_AttachLastArc()
#define gp_BindFirstArc(theGraph, v, arc) \
{ \
gp_SetPrevArc(theGraph, arc, NIL); \
gp_SetFirstArc(theGraph, v, arc); \
}
#define gp_BindLastArc(theGraph, v, arc) \
{ \
gp_SetNextArc(theGraph, arc, NIL); \
gp_SetLastArc(theGraph, v, arc); \
}
// Attaches an arc between the current binding between a vertex and its first arc
#define gp_AttachFirstArc(theGraph, v, arc) \
{ \
if (gp_IsArc(gp_GetFirstArc(theGraph, v))) \
{ \
gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \
gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \
} \
else gp_BindLastArc(theGraph, v, arc); \
gp_BindFirstArc(theGraph, v, arc); \
}
// Attaches an arc between the current binding betwen a vertex and its last arc
#define gp_AttachLastArc(theGraph, v, arc) \
{ \
if (gp_IsArc(gp_GetLastArc(theGraph, v))) \
{ \
gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \
gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \
} \
else gp_BindFirstArc(theGraph, v, arc); \
gp_BindLastArc(theGraph, v, arc); \
}
// Moves an arc that is in the adjacency list of v to the start of the adjacency list
#define gp_MoveArcToFirst(theGraph, v, arc) \
if (arc != gp_GetFirstArc(theGraph, v)) \
{ \
/* If the arc is last in the adjacency list of uparent,
then we delete it by adjacency list end management */ \
if (arc == gp_GetLastArc(theGraph, v)) \
{ \
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), NIL); \
gp_SetLastArc(theGraph, v, gp_GetPrevArc(theGraph, arc)); \
} \
/* Otherwise, we delete the arc from the middle of the list */ \
else \
{ \
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \
} \
\
/* Now add arc e as the new first arc of uparent.
Note that the adjacency list is non-empty at this time */ \
gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \
gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \
gp_BindFirstArc(theGraph, v, arc); \
}
// Moves an arc that is in the adjacency list of v to the end of the adjacency list
#define gp_MoveArcToLast(theGraph, v, arc) \
if (arc != gp_GetLastArc(theGraph, v)) \
{ \
/* If the arc is first in the adjacency list of vertex v,
then we delete it by adjacency list end management */ \
if (arc == gp_GetFirstArc(theGraph, v)) \
{ \
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), NIL); \
gp_SetFirstArc(theGraph, v, gp_GetNextArc(theGraph, arc)); \
} \
/* Otherwise, we delete the arc from the middle of the list */ \
else \
{ \
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \
} \
\
/* Now add the arc as the new last arc of v.
Note that the adjacency list is non-empty at this time */ \
gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \
gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \
gp_BindLastArc(theGraph, v, arc); \
}
// Methods for attaching an arc into the adjacency list or detaching an arc from it.
// The terms AddArc, InsertArc and DeleteArc are not used because the arcs are not
// inserted or added to or deleted from storage (only whole edges are inserted or deleted)
void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc);
void gp_DetachArc(graphP theGraph, int arc);
/********************************************************************
PERTINENT()
A vertex is pertinent in a partially processed graph if there is an
unprocessed back edge between the vertex v whose edges are currently
being processed and either the vertex or a DFS descendant D of the
vertex not in the same bicomp as the vertex.
The vertex is either directly adjacent to v by an unembedded back edge
or there is an unembedded back edge (v, D) and the vertex is a cut
vertex in the partially processed graph along the DFS tree path from
D to v.
Pertinence is a dynamic property that can change for a vertex after
each edge addition. In other words, a vertex can become non-pertinent
during step v as more back edges to v are embedded.
********************************************************************/
#define PERTINENT(theGraph, theVertex) \
(gp_IsArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) || \
gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex)))
#define NOTPERTINENT(theGraph, theVertex) \
(gp_IsNotArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) && \
gp_IsNotVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex)))
/********************************************************************
FUTUREPERTINENT()
A vertex is future-pertinent in a partially processed graph if
there is an unprocessed back edge between a DFS ancestor A of the
vertex v whose edges are currently being processed and either
theVertex or a DFS descendant D of theVertex not in the same bicomp
as theVertex.
Either theVertex is directly adjacent to A by an unembedded back edge
or there is an unembedded back edge (A, D) and theVertex is a cut
vertex in the partially processed graph along the DFS tree path from
D to A.
If no more edges are added to the partially processed graph prior to
processing the edges of A, then the vertex would be pertinent.
The addition of edges to the partially processed graph can alter
both the pertinence and future pertinence of a vertex. For example,
if the vertex is pertinent due to an unprocessed back edge (v, D1) and
future pertinent due to an unprocessed back edge (A, D2), then the
vertex may lose both its pertinence and future pertinence when edge
(v, D1) is added if D2 is in the same subtree as D1.
Generally, pertinence and future pertinence are dynamic properties
that can change for a vertex after each edge addition.
Note that gp_UpdateVertexFuturePertinentChild() must be called before
this macro. Since it is a statement and not a void expression, the
desired commented out version does not compile (except with special
compiler extensions not assumed by this code).
********************************************************************/
#define FUTUREPERTINENT(theGraph, theVertex, v) \
( theGraph->VI[theVertex].leastAncestor < v || \
(gp_IsVertex(theGraph->VI[theVertex].futurePertinentChild) && \
theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) )
#define NOTFUTUREPERTINENT(theGraph, theVertex, v) \
( theGraph->VI[theVertex].leastAncestor >= v && \
(gp_IsNotVertex(theGraph->VI[theVertex].futurePertinentChild) || \
theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint >= v) )
// This is the definition that would be preferrable if a while loop could be a void expression
//#define FUTUREPERTINENT(theGraph, theVertex, v)
// ( theGraph->VI[theVertex].leastAncestor < v ||
// ((gp_UpdateVertexFuturePertinentChild(theGraph, theVertex, v),
// gp_IsArc(theGraph->VI[theVertex].futurePertinentChild)) &&
// theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) )
/********************************************************************
INACTIVE()
For planarity algorithms, a vertex is inactive if it is neither pertinent
nor future pertinent.
********************************************************************/
#define INACTIVE(theGraph, theVertex, v) \
( NOTPERTINENT(theGraph, theVertex) && \
NOTFUTUREPERTINENT(theGraph, theVertex, v))
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/graphTests.c 0000664 0000000 0000000 00000107503 12716155161 0023734 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#define GRAPHTEST_C
#include "graph.h"
#include "stack.h"
extern void _ClearVertexVisitedFlags(graphP theGraph, int);
/* Private function declarations */
int _TestPath(graphP theGraph, int U, int V);
int _TryPath(graphP theGraph, int e, int V);
void _MarkPath(graphP theGraph, int e);
int _TestSubgraph(graphP theSubgraph, graphP theGraph);
int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _CheckEmbeddingFacialIntegrity(graphP theGraph);
int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
int _CheckKuratowskiSubgraphIntegrity(graphP theGraph);
int _CheckOuterplanarObstructionIntegrity(graphP theGraph);
int _CheckAllVerticesOnExternalFace(graphP theGraph);
void _MarkExternalFaceVertices(graphP theGraph, int startVertex);
/********************************************************************
gp_TestEmbedResultIntegrity()
This function tests the integrity of the graph result returned
from gp_Embed().
The caller of gp_Embed() does not have to save the original graph
because, for efficiency, gp_Embed() operates on the input graph.
However, to test the integrity of the result relative to the input,
a copy of the input graph is required.
Modules that extend/alter the behavior of gp_Embed() beyond the
core planarity embedder and planarity obstruction isolator should
also provide overriding integrity test routines appropriate to the
extension algorithm.
The main method first calls gp_SortVertices on theGraph, if the
origGraph is not in DFI order (the common case). Therefore,
extension integrity tests can count on a consistent numbering
between theGraph and the origGraph, either DFI order or pre-DFS
order if that is the state of the origGraph.
After all tests, the main method ensures theGraph is restored to
DFI order by invoking gp_SortVertices if needed, thus ensuring
that theGraph has the documented post-condition of gp_Embed().
For an embedResult of OK, fpCheckEmbeddingIntegrity is invoked.
The core planarity implementation does a face walk of all faces
of the embedding. It ensures that all edges were used in the face
walk and that the right number of faces exist for the number of
vertices and edges. Also, we ensure that all adjacencies expressed
in the original graph still exist in the result graph.
For an embedResult of NONEMBEDDABLE, fpCheckObstructionIntegrity
is invoked. The core planarity algorithm checks that the result
graph is homeomorphic to K5 or K3,3 and that it is in fact a
subgraph of the input graph. Other algorithms use overloads to
make appropriate checks.
Returns NOTOK on integrity check failure or embedResult of NOTOK
OK for successful integrity check of OK embedResult
NONEMBEDDABLE for successful integrity check of an
embedResult of NONEMBEDDABLE
********************************************************************/
int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult)
{
int RetVal = embedResult;
if (theGraph == NULL || origGraph == NULL)
return NOTOK;
if (embedResult == OK)
{
RetVal = theGraph->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);
}
else if (embedResult == NONEMBEDDABLE)
{
RetVal = theGraph->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
}
if (RetVal == OK)
RetVal = embedResult;
return RetVal;
}
/********************************************************************
_CheckEmbeddingIntegrity()
The core planarity implementation does a face walk of all faces
of the embedding. It ensures that all edges were used in the face
walk and that the right number of faces exist for the number of
vertices and edges. Also, we ensure that all adjacencies expressed
in the original graph still exist in the result graph, accounting
for the fact that the result graph is sorted by DFI, but the input
may or may not be sorted by DFI.
returns OK if all integrity tests passed, NOTOK otherwise
********************************************************************/
int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
{
if (theGraph == NULL || origGraph == NULL)
return NOTOK;
if (_TestSubgraph(theGraph, origGraph) != TRUE)
return NOTOK;
if (_TestSubgraph(origGraph, theGraph) != TRUE)
return NOTOK;
if (_CheckEmbeddingFacialIntegrity(theGraph) != OK)
return NOTOK;
if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)
{
if (_CheckAllVerticesOnExternalFace(theGraph) != OK)
return NOTOK;
}
return OK;
}
/********************************************************************
_CheckEmbeddingFacialIntegrity()
This function traverses all faces of a graph structure containing
the planar embedding that results from gp_Embed(). The algorithm
begins by placing all of the graph's arcs onto a stack and marking
all of them as unvisited. For each arc popped, if it is visited,
it is immediately discarded and the next arc is popped. Popping an
unvisited arc e begins a face traversal. We move to the true twin
arc of e, and obtain its successor arc. This amounts to always
going clockwise or counterclockwise (depending on how the graph is
drawn on the plane, or alternately whether one is above or below
the plane). This traversal continues until we make it back to the
original arc e. Each arc along the way is marked as visited. Further,
if the successor arc has been visited, then there is an error since
an arc can only appear in one face (the twin arc appears in a separate
face, which is traversed in the opposing direction).
If this algorithm succeeds without double visiting any arcs, and it
produces the correct face count according to Euler's formula, then
the embedding has all vertices oriented the same way.
NOTE: In disconnected graphs, the face reader counts the external
face of each connected component. So, we adjust the face
count by subtracting one for each component, then we add one
to count the external face shared by all components.
********************************************************************/
int _CheckEmbeddingFacialIntegrity(graphP theGraph)
{
stackP theStack = theGraph->theStack;
int EsizeOccupied, v, e, eTwin, eStart, eNext, NumFaces, connectedComponents;
if (theGraph == NULL)
return NOTOK;
/* The stack need only contain 2M entries, one for each edge record. With
max M at 3N, this amounts to 6N integers of space. The embedding
structure already contains this stack, so we just make sure it
starts out empty. */
sp_ClearStack(theStack);
/* Push all arcs and set them to unvisited */
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2)
{
// Except skip edge holes
if (gp_EdgeInUse(theGraph, e))
{
sp_Push(theStack, e);
gp_ClearEdgeVisited(theGraph, e);
eTwin = gp_GetTwinArc(theGraph, e);
sp_Push(theStack, eTwin);
gp_ClearEdgeVisited(theGraph, eTwin);
}
}
// There are M edges, so we better have pushed 2M arcs just now
// i.e. testing that the continue above skipped only edge holes
if (sp_GetCurrentSize(theStack) != 2*theGraph->M)
return NOTOK;
/* Read faces until every arc is used */
NumFaces = 0;
while (sp_NonEmpty(theStack))
{
/* Get an arc; if it has already been used by a face, then
don't use it to traverse a new face */
sp_Pop(theStack, eStart);
if (gp_GetEdgeVisited(theGraph, eStart)) continue;
e = eStart;
do {
eNext = gp_GetNextArcCircular(theGraph, gp_GetTwinArc(theGraph, e));
if (gp_GetEdgeVisited(theGraph, eNext))
return NOTOK;
gp_SetEdgeVisited(theGraph, eNext);
e = eNext;
} while (e != eStart);
NumFaces++;
}
/* Count the external face once rather than once per connected component;
each connected component is detected by the fact that it has no
DFS parent, except in the case of isolated vertices, no face was counted
so we do not subtract one. */
connectedComponents = 0;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
if (gp_IsDFSTreeRoot(theGraph, v))
{
if (gp_GetVertexDegree(theGraph, v) > 0)
NumFaces--;
connectedComponents++;
}
}
NumFaces++;
/* Test number of faces using the extended Euler's formula.
For connected components, Euler's formula is f=m-n+2, but
for disconnected graphs it is extended to f=m-n+1+c where
c is the number of connected components.*/
return NumFaces == theGraph->M - theGraph->N + 1 + connectedComponents
? OK : NOTOK;
}
/********************************************************************
_CheckAllVerticesOnExternalFace()
Determines whether or not any vertices have been embedded within
the bounding cycle of the external face.
The input graph may be disconnected, so this routine walks the
external face starting at each vertex with no DFSParent.
return OK if all vertices visited on external face walks, NOTOK otherwise
********************************************************************/
int _CheckAllVerticesOnExternalFace(graphP theGraph)
{
int v;
// Mark all vertices unvisited
_ClearVertexVisitedFlags(theGraph, FALSE);
// For each connected component, walk its external face and
// mark the vertices as visited
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
if (gp_IsDFSTreeRoot(theGraph, v))
_MarkExternalFaceVertices(theGraph, v);
}
// If any vertex is unvisited, then the embedding is not an outerplanar
// embedding, so we return NOTOK
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
if (!gp_GetVertexVisited(theGraph, v))
return NOTOK;
// All vertices were found on external faces of the connected components
// so the embedding is an outerplanar embedding and we return OK
return OK;
}
/********************************************************************
_MarkExternalFaceVertices()
Walks the external face of the connected component containing the
start vertex, and marks the visited flag of all vertices found.
The start vertex is assumed to be on the external face.
This method assumed the embedding integrity has already been
verified to be correct.
This method correctly handles components that have cut vertices,
i.e. it does not assume that the outer face is a simple cycle;
it only assumes that all vertices are reachable by walking a
single face that starts with startVertex.
********************************************************************/
void _MarkExternalFaceVertices(graphP theGraph, int startVertex)
{
int nextVertex = startVertex;
int e = gp_GetFirstArc(theGraph, nextVertex);
int eTwin;
// Handle the case of an isolated vertex
if (gp_IsNotArc(e))
{
gp_SetVertexVisited(theGraph, startVertex);
return;
}
// Process a non-trivial connected component
do {
gp_SetVertexVisited(theGraph, nextVertex);
// The arc out of the vertex just visited points to the next vertex
nextVertex = gp_GetNeighbor(theGraph, e);
// Arc used to enter the next vertex is needed so we can get the
// next edge in rotation order.
// Note: for bicomps, first and last arcs of all external face vertices
// indicate the edges that hold them to the external face
// But _JoinBicomps() has already occurred, so cut vertices
// will have external face edges other than the first and last arcs
// Hence we need this more sophisticated traversal method
eTwin = gp_GetTwinArc(theGraph, e);
// Now we get the next arc in rotation order as the new arc out to the
// vertex after nextVertex. This sets us up for the next iteration.
// Note: We cannot simply follow the chain of nextVertex first arcs
// as we started out doing at the top of this method. This is
// because we are no longer dealing with bicomps only.
// Since _JoinBicomps() has already been invoked, there may now
// be cut vertices on the external face whose adjacency lists
// contain external face arcs in positions other than the first and
// and last arcs. We will visit those vertices multiple times,
// which is OK (just that we have to explain why we're calculating
// jout in this way).
e = gp_GetNextArcCircular(theGraph, eTwin);
// Now things get really interesting. The DFS root (startVertex) may
// itself be a cut vertex to which multiple bicomps have been joined.
// So we cannot simply stop when the external face walk gets back to
// startVertex. We must actually get back to startVertex using its
// last arc. This ensures that we've looped down into all the DFS
// subtrees rooted at startVertex and walked their external faces.
// Since we started the whole external face walk with the first arc
// of startVertex, we need to proceed until we reenter startVertex
// using its last arc.
} while (eTwin != gp_GetLastArc(theGraph, startVertex));
}
/********************************************************************
_CheckObstructionIntegrity()
Returns OK if theGraph is a subgraph of origGraph and it contains
an allowed homeomorph, and NOTOK otherwise.
For core planarity, the allowed homeomorphs are K_5 or K_{3,3}
Extension modules may overload this method to implement different
tests. For example, K_{3,3} search allows only K_{3,3} and
outerplanarity allows only K_4 or K_{2,3}.
********************************************************************/
int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
{
if (theGraph == NULL || origGraph == NULL)
return NOTOK;
if (_TestSubgraph(theGraph, origGraph) != TRUE)
{
return NOTOK;
}
if (theGraph->embedFlags == EMBEDFLAGS_PLANAR)
return _CheckKuratowskiSubgraphIntegrity(theGraph);
else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)
return _CheckOuterplanarObstructionIntegrity(theGraph);
return NOTOK;
}
/********************************************************************
_getImageVertices()
Count the number of vertices of each degree and find the locations of
the image vertices (also sometimes called the corners of the obstruction).
An image vertex is a vertex of degree three or higher because degree
2 vertices are generally internal to the paths between the image
vertices.
The notable exception is K_{2,3}, an obstruction to outerplanarity.
This routine does not know the obstruction it is looking for, so the
caller must decide whether there are any degree 2 vertices that should
be added to imageVerts.
Return NOTOK if any vertex of degree 1 or higher than the max is found
NOTOK if more than the max number of image vertices is found.
Return OK otherwise.
********************************************************************/
int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,
int *imageVerts, int maxNumImageVerts)
{
int K, v, imageVertPos, degree;
for (degree = 0; degree <= maxDegree; degree++)
degrees[degree] = 0;
for (K = 0; K < maxNumImageVerts; K++)
imageVerts[K] = NIL;
imageVertPos = 0;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
degree = gp_GetVertexDegree(theGraph, v);
if (degree == 1)
return NOTOK;
if (degree > maxDegree)
return NOTOK;
degrees[degree]++;
if (imageVertPos < maxNumImageVerts && degree > 2)
imageVerts[imageVertPos++] = v;
else if (degree > 2)
return NOTOK;
}
return OK;
}
/********************************************************************
_TestForCompleteGraphObstruction()
theGraph - the graph to test
numVerts - the number of image vertices (corners) of the complete
graph homeomorph being tested for (e.g. 5 for a K5)
degrees - array of counts of the number of vertices of each degree
given by the array index. Only valid up to numVerts-1
imageVerts - the vertices of degree numVerts-1
This routine tests whether theGraph is a K_{numVerts} homeomorph for
numVerts >= 4.
returns FALSE if numVerts < 4,
if theGraph has other than numVerts image vertices
if theGraph contains other than degree 2 vertices plus
the image vertices
if any pair of image vertices lacks a connecting path
if any degree two vertices are not in the connecting paths
TRUE otherwise
********************************************************************/
int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts,
int *degrees, int *imageVerts)
{
int v, w;
// We need to make sure we have numVerts vertices of degree numVerts-1
// For example, if numVerts==5, then we're looking for a K5, so we
// need to have degrees[4] == 5 (5 vertices of degree 4)
if (degrees[numVerts-1] != numVerts)
return FALSE;
// All vertices need to be degree 0, degree 2 or degree numVerts-1
if (degrees[0]+degrees[2]+degrees[numVerts-1] != theGraph->N)
return FALSE;
// We clear all the vertex visited flags
_ClearVertexVisitedFlags(theGraph, FALSE);
// For each pair of image vertices, we test that there is a path
// between the two vertices. If so, the visited flags of the
// internal vertices along the path are marked
//
for (v = 0; v < numVerts; v++)
for (w = 0; w < numVerts; w++)
if (v != w)
if (_TestPath(theGraph, imageVerts[v],
imageVerts[w]) != TRUE)
return FALSE;
// The visited flags should have marked only degree two vertices,
// so for every marked vertex, we subtract one from the count of
// the degree two vertices.
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
if (gp_GetVertexVisited(theGraph, v))
degrees[2]--;
/* If every degree 2 vertex is used in a path between image
vertices, then there are no extra pieces of the graph
in theGraph. Specifically, the prior tests identify
a K_5 and ensure that nothing else could exist in the
graph except extra degree 2 vertices, which must be
joined in a cycle so that all are degree 2. */
return degrees[2] == 0 ? TRUE : FALSE;
}
/********************************************************************
_TestForK33GraphObstruction()
theGraph - the graph to test
degrees - array of counts of the number of vertices of each degree
given by the array index. Only valid up to numVerts-1
imageVerts - the degree 3 vertices of the K3,3 homeomorph
This routine tests whether theGraph is a K_{3,3} homeomorph.
returns TRUE if so, FALSE if not
********************************************************************/
int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts)
{
int v, K, imageVertPos, temp, success;
if (degrees[4] != 0)
return FALSE;
if (degrees[3] != 6)
return FALSE;
/* Partition the six image vertices into two sets of 3
(or report failure) */
for (imageVertPos = 3; imageVertPos < 6; imageVertPos++)
{
K = 0;
success = FALSE;
do {
if (_TestPath(theGraph, imageVerts[imageVertPos], imageVerts[0]) == TRUE)
{
success = TRUE;
break;
}
K++;
temp = imageVerts[K];
imageVerts[K] = imageVerts[imageVertPos];
imageVerts[imageVertPos] = temp;
} while (K < 3);
if (!success)
return FALSE;
}
/* Now test the paths between each of the first three vertices and
each of the last three vertices */
_ClearVertexVisitedFlags(theGraph, FALSE);
for (imageVertPos=0; imageVertPos<3; imageVertPos++)
for (K=3; K<6; K++)
if (_TestPath(theGraph, imageVerts[imageVertPos],
imageVerts[K]) != TRUE)
return FALSE;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
if (gp_GetVertexVisited(theGraph, v))
degrees[2]--;
/* If every degree 2 vertex is used in a path between image
vertices, then there are no extra pieces of the graph
in theGraph. Specifically, the prior tests identify
a K_{3,3} and ensure that nothing else could exist in the
graph except extra degree 2 vertices, which must be
joined in a cycle so that all are degree 2. */
return degrees[2] == 0 ? TRUE : FALSE;
}
/********************************************************************
_CheckKuratowskiSubgraphIntegrity()
This function checks whether theGraph received as input contains
either a K_5 or K_{3,3} homeomorph.
RETURNS: OK if theGraph contains a K_5 or K_{3,3} homeomorph,
NOTOK otherwise
To be a K_5 homeomorph, there must be exactly 5 vertices of degree 4,
which are called 'image' vertices, and all other vertices must be
either degree 2 or degree 0. Furthermore, each of the image vertices
must be able to reach all of the other image vertices by a single edge
or a path of degree two vertices.
To be a K_{3,3} homeomorph, there must be exactly 6 vertices of degree 3,
which are called 'image' vertices, and all other vertices must be either
degree 2 or degree 0. Furthermore, the image vertices must be connected
by edges or paths of degree two vertices in the manner suggested by
a K_{3,3}. To test this, we select an image vertex U, and determine
three image vertices X, Y and Z reachable from U by single edges or
paths of degree 2 vertices. Then, we check that the two remaining image
vertices, V and W, can also reach X, Y and Z by single edges or paths of
degree 2 vertices.
It is not necessary to check that the paths between the image vertices
are distinct since if the paths had a common vertex, then the common
vertex would not be degree 2.
********************************************************************/
int _CheckKuratowskiSubgraphIntegrity(graphP theGraph)
{
int degrees[5], imageVerts[6];
if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK)
return NOTOK;
if (_TestForCompleteGraphObstruction(theGraph, 5, degrees, imageVerts) == TRUE)
{
return OK;
}
if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE)
{
return OK;
}
return NOTOK;
}
/********************************************************************
_TestForK23GraphObstruction()
theGraph - the graph to test
degrees - array of counts of the number of vertices of each degree
given by the array index. Only valid up to numVerts-1
imageVerts - the degree 3 vertices of the K2,3 homeomorph
This routine tests whether theGraph is a K_{2,3} homeomorph.
This routine operates over the results of _getImageVertices()
returns TRUE if so, FALSE if not
********************************************************************/
int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts)
{
int v, e, imageVertPos;
// This function operates over the imageVerts results produced by
// getImageVertices, which only finds vertices of degree 3 or higher.
// So, for a K2,3, there must be exactly two degree 3 vertices and
// no degree 4 vertices.
if (degrees[3] != 2)
return FALSE;
// For K_{2,3}, the three vertices of degree 2 were not
// detected as image vertices because degree 2 vertices
// are indistinguishable from the internal path vertices
// between the image vertices. So, here we acknowledge
// that more image vertices need to be selected.
imageVertPos = 2;
// Assign the remaining three image vertices to be the
// neighbors of the first degree 3 image vertex.
// Ensure that each is distinct from the second
// degree 3 image vertex. This must be the case because
// the two degree 3 image vertices are in the same partition
// and hence must not be adjacent.
e = gp_GetFirstArc(theGraph, imageVerts[0]);
while (gp_IsArc(e))
{
imageVerts[imageVertPos] = gp_GetNeighbor(theGraph, e);
if (imageVerts[imageVertPos] == imageVerts[1])
return FALSE;
imageVertPos++;
e = gp_GetNextArc(theGraph, e);
}
/* The paths from imageVerts[0] to each of the new degree 2
image vertices are the edges we just traversed.
Now test the paths between each of the degree 2 image
vertices and imageVerts[1]. */
_ClearVertexVisitedFlags(theGraph, FALSE);
for (imageVertPos=2; imageVertPos<5; imageVertPos++)
{
if (_TestPath(theGraph, imageVerts[imageVertPos],
imageVerts[1]) != TRUE)
return FALSE;
gp_SetVertexVisited(theGraph, imageVerts[imageVertPos]);
}
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
if (gp_GetVertexVisited(theGraph, v))
degrees[2]--;
/* If every degree 2 vertex is used in a path between the
two degree 3 image vertices, then there are no extra
pieces of the graph in theGraph. Specifically, the
prior tests identify a K_{2,3} and ensure that nothing
else could exist in the graph... except extra degree 2
vertices joined in a cycle. We return NOTOK in that case. */
return degrees[2] == 0 ? TRUE : FALSE;
}
/********************************************************************
_CheckOuterplanarObstructionIntegrity()
This function checks whether theGraph received as input contains
either a K_4 or K_{2,3} homeomorph.
RETURNS: OK if theGraph contains a K_4 or K_{2,3} homeomorph,
NOTOK otherwise
To be a K_4 homeomorph, there must be exactly 4 vertices of degree 3,
which are called 'image' vertices, and all other vertices must be
either degree 2 or degree 0. Furthermore, each of the image vertices
must be able to reach all of the other image vertices by a single edge
or a path of degree two vertices.
To be a K_{2,3} homeomorph, there must be exactly 2 vertices of degree 3.
All other vertices must be degree 2. Furthermore, the two degree 3
vertices must have three internally disjoint paths connecting them,
and each path must contain at least two edges (i.e. at least one internal
vertex). The two degree 3 vertices are image vertices, and an internal
vertex from each of the three paths contributes the remaining three
image vertices.
It is not necessary to check that the paths between the degree three
vertices are distinct since if the paths had a common vertex, then the
common vertex would not be degree 2.
********************************************************************/
int _CheckOuterplanarObstructionIntegrity(graphP theGraph)
{
int degrees[4], imageVerts[5];
if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK)
return NOTOK;
if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE)
{
return OK;
}
if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE)
{
return OK;
}
/* We get here only if we failed to recognize an outerplanarity
obstruction, so we return failure */
return NOTOK;
}
/********************************************************************
_TestPath()
This function determines whether there exists a path of degree two
vertices between two given vertices. The function marks each
degree two vertex as visited. It returns TRUE if it finds the
path and FALSE otherwise.
********************************************************************/
int _TestPath(graphP theGraph, int U, int V)
{
int e = gp_GetFirstArc(theGraph, U);
while (gp_IsArc(e))
{
if (_TryPath(theGraph, e, V) == OK)
{
_MarkPath(theGraph, e);
return TRUE;
}
e = gp_GetNextArc(theGraph, e);
}
return FALSE;
}
/********************************************************************
_TryPath()
This function seeks a given path to a vertex V starting with a
given edge out of a starting vertex U. The path is allowed to
contain zero or more degree two vertices, but we stop as soon as
a vertex of degree higher than two is encountered.
The function returns boolean true if that vertex is V, and
boolean false otherwise.
********************************************************************/
int _TryPath(graphP theGraph, int e, int V)
{
int eTwin, nextVertex;
nextVertex = gp_GetNeighbor(theGraph, e);
// while nextVertex is strictly degree 2
while (gp_IsArc(gp_GetFirstArc(theGraph, nextVertex)) &&
gp_IsArc(gp_GetLastArc(theGraph, nextVertex)) &&
gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex))
{
eTwin = gp_GetTwinArc(theGraph, e);
e = gp_GetFirstArc(theGraph, nextVertex);
if (e == eTwin)
e = gp_GetLastArc(theGraph, nextVertex);
nextVertex = gp_GetNeighbor(theGraph, e);
}
return nextVertex == V ? TRUE : FALSE;
}
/********************************************************************
_MarkPath()
This function sets the visitation flag on all degree two vertices
along a path to a vertex V that starts with a given edge out of
a starting vertex U.
********************************************************************/
void _MarkPath(graphP theGraph, int e)
{
int eTwin, nextVertex;
nextVertex = gp_GetNeighbor(theGraph, e);
// while nextVertex is strictly degree 2
while (gp_IsArc(gp_GetFirstArc(theGraph, nextVertex)) &&
gp_IsArc(gp_GetLastArc(theGraph, nextVertex)) &&
gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex))
{
gp_SetVertexVisited(theGraph, nextVertex);
eTwin = gp_GetTwinArc(theGraph, e);
e = gp_GetFirstArc(theGraph, nextVertex);
if (e == eTwin)
e = gp_GetLastArc(theGraph, nextVertex);
nextVertex = gp_GetNeighbor(theGraph, e);
}
}
/********************************************************************
_TestSubgraph()
Checks whether theSubgraph is in fact a subgraph of theGraph.
For each vertex v in graph G and subgraph H, we iterate the adjacency
list of H(v) and, for each neighbor w, we mark G(w). Then, we
iterate the adjacency list of G(v) and unmark each neighbor. Then,
we iterate the adjacency list of H(v) again to ensure that every
neighbor w was unmarked. If there exists a marked neighbor, then
H(v) contains an incident edge that is not incident to G(v).
Returns TRUE if theSubgraph contains only edges from theGraph,
FALSE otherwise
********************************************************************/
int _TestSubgraph(graphP theSubgraph, graphP theGraph)
{
int v, e, degreeCount;
int Result = TRUE;
int invokeSortOnGraph = FALSE;
int invokeSortOnSubgraph = FALSE;
// If the graph is not sorted by DFI, but the alleged subgraph is,
// then "unsort" the alleged subgraph so both have the same vertex order
if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI) &&
(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI))
{
invokeSortOnSubgraph = TRUE;
gp_SortVertices(theSubgraph);
}
// If the graph is not sorted by DFI, but the alleged subgraph is,
// then "unsort" the alleged subgraph so both have the same vertex order
if (!(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI) &&
(theGraph->internalFlags & FLAGS_SORTEDBYDFI))
{
invokeSortOnGraph = TRUE;
gp_SortVertices(theGraph);
}
/* We clear all visitation flags */
_ClearVertexVisitedFlags(theGraph, FALSE);
/* For each vertex... */
for (v = gp_GetFirstVertex(theSubgraph), degreeCount = 0; gp_VertexInRange(theSubgraph, v); v++)
{
/* For each neighbor w in the adjacency list of vertex v in the
subgraph, set the visited flag in w in the graph */
e = gp_GetFirstArc(theSubgraph, v);
while (gp_IsArc(e))
{
if (gp_IsNotVertex(gp_GetNeighbor(theSubgraph, e)))
{
Result = FALSE;
break;
}
degreeCount++;
gp_SetVertexVisited(theGraph, gp_GetNeighbor(theSubgraph, e));
e = gp_GetNextArc(theSubgraph, e);
}
if (Result != TRUE)
break;
/* For each neighbor w in the adjacency list of vertex v in the graph,
clear the visited flag in w in the graph */
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_IsNotVertex(gp_GetNeighbor(theGraph, e)))
{
Result = FALSE;
break;
}
gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
if (Result != TRUE)
break;
/* For each neighbor w in the adjacency list of vertex v in the subgraph,
ensure that the visited flag in w was cleared (otherwise, the "subgraph"
would incorrectly contain an adjacency not contained in the ("super") graph) */
e = gp_GetFirstArc(theSubgraph, v);
while (gp_IsArc(e))
{
if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theSubgraph, e)))
{
Result = FALSE;
break;
}
e = gp_GetNextArc(theSubgraph, e);
}
if (Result != TRUE)
break;
}
// Restore the DFI sort order of either graph if it had to be reordered at the start
if (invokeSortOnSubgraph)
gp_SortVertices(theSubgraph);
if (invokeSortOnGraph)
gp_SortVertices(theGraph);
// Assuming theSubgraph is a subgraph, we also do an extra integrity check to ensure
// proper edge array utilization
if (Result == TRUE)
{
// If the edge count is wrong, we fail the subgraph test in a way that invokes
// the name NOTOK so that in debug mode there is more trace on the failure.
if (degreeCount != 2*theSubgraph->M)
Result = NOTOK == FALSE ? NOTOK : FALSE;
}
return Result;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/graphUtils.c 0000664 0000000 0000000 00000256254 12716155161 0023742 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include
#include "graphStructures.h"
#include "graph.h"
/* Imported functions for FUNCTION POINTERS */
extern int _EmbeddingInitialize(graphP theGraph);
extern int _SortVertices(graphP theGraph);
extern void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
extern void _WalkUp(graphP theGraph, int v, int e);
extern int _WalkDown(graphP theGraph, int v, int RootVertex);
extern int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink);
extern void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R);
extern int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R);
extern int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink);
extern int _MarkDFSPath(graphP theGraph, int ancestor, int descendant);
extern int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
extern int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
extern int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
extern int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize);
extern int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize);
/* Internal util functions for FUNCTION POINTERS */
int _HideVertex(graphP theGraph, int vertex);
void _HideEdge(graphP theGraph, int arcPos);
void _RestoreEdge(graphP theGraph, int arcPos);
int _ContractEdge(graphP theGraph, int e);
int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore);
int _RestoreVertex(graphP theGraph);
/********************************************************************
Private functions, except exported within library
********************************************************************/
void _InitIsolatorContext(graphP theGraph);
void _ClearVisitedFlags(graphP theGraph);
void _ClearVertexVisitedFlags(graphP theGraph, int);
void _ClearEdgeVisitedFlags(graphP theGraph);
int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph);
int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue);
int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot);
int _HideInternalEdges(graphP theGraph, int vertex);
int _RestoreInternalEdges(graphP theGraph, int stackBottom);
int _RestoreHiddenEdges(graphP theGraph, int stackBottom);
int _GetBicompSize(graphP theGraph, int BicompRoot);
int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot);
int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot);
void _InitFunctionTable(graphP theGraph);
/********************************************************************
Private functions.
********************************************************************/
void _InitVertices(graphP theGraph);
void _InitEdges(graphP theGraph);
void _ClearGraph(graphP theGraph);
int _GetRandomNumber(int NMin, int NMax);
/* Private functions for which there are FUNCTION POINTERS */
void _InitVertexRec(graphP theGraph, int v);
void _InitVertexInfo(graphP theGraph, int v);
void _InitEdgeRec(graphP theGraph, int e);
int _InitGraph(graphP theGraph, int N);
void _ReinitializeGraph(graphP theGraph);
int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);
/********************************************************************
gp_New()
Constructor for graph object.
Can create two graphs if restricted to no dynamic memory.
********************************************************************/
graphP gp_New()
{
graphP theGraph = (graphP) malloc(sizeof(baseGraphStructure));
if (theGraph != NULL)
{
theGraph->E = NULL;
theGraph->V = NULL;
theGraph->VI = NULL;
theGraph->BicompRootLists = NULL;
theGraph->sortedDFSChildLists = NULL;
theGraph->theStack = NULL;
theGraph->extFace = NULL;
theGraph->edgeHoles = NULL;
theGraph->extensions = NULL;
_InitFunctionTable(theGraph);
_ClearGraph(theGraph);
}
return theGraph;
}
/********************************************************************
_InitFunctionTable()
If you add functions to the function table, then they must be
initialized here, but you must also add the new function pointer
to the definition of the graphFunctionTable in graphFunctionTable.h
Function headers for the functions used to initialize the table are
classified at the top of this file as either imported from other
compilation units (extern) or private to this compilation unit.
Search for FUNCTION POINTERS in this file to see where to add the
function header.
********************************************************************/
void _InitFunctionTable(graphP theGraph)
{
theGraph->functions.fpEmbeddingInitialize = _EmbeddingInitialize;
theGraph->functions.fpEmbedBackEdgeToDescendant = _EmbedBackEdgeToDescendant;
theGraph->functions.fpWalkUp = _WalkUp;
theGraph->functions.fpWalkDown = _WalkDown;
theGraph->functions.fpMergeBicomps = _MergeBicomps;
theGraph->functions.fpMergeVertex = _MergeVertex;
theGraph->functions.fpHandleBlockedBicomp = _HandleBlockedBicomp;
theGraph->functions.fpHandleInactiveVertex = _HandleInactiveVertex;
theGraph->functions.fpEmbedPostprocess = _EmbedPostprocess;
theGraph->functions.fpMarkDFSPath = _MarkDFSPath;
theGraph->functions.fpCheckEmbeddingIntegrity = _CheckEmbeddingIntegrity;
theGraph->functions.fpCheckObstructionIntegrity = _CheckObstructionIntegrity;
theGraph->functions.fpInitGraph = _InitGraph;
theGraph->functions.fpReinitializeGraph = _ReinitializeGraph;
theGraph->functions.fpEnsureArcCapacity = _EnsureArcCapacity;
theGraph->functions.fpSortVertices = _SortVertices;
theGraph->functions.fpReadPostprocess = _ReadPostprocess;
theGraph->functions.fpWritePostprocess = _WritePostprocess;
theGraph->functions.fpHideEdge = _HideEdge;
theGraph->functions.fpRestoreEdge = _RestoreEdge;
theGraph->functions.fpHideVertex = _HideVertex;
theGraph->functions.fpRestoreVertex = _RestoreVertex;
theGraph->functions.fpContractEdge = _ContractEdge;
theGraph->functions.fpIdentifyVertices = _IdentifyVertices;
}
/********************************************************************
gp_InitGraph()
Allocates memory for vertex and edge records now that N is known.
The arcCapacity is set to (2 * DEFAULT_EDGE_LIMIT * N) unless it
has already been set by gp_EnsureArcCapacity()
For V, we need 2N vertex records, N for vertices and N for virtual vertices (root copies).
For VI, we need N vertexInfo records.
For E, we need arcCapacity edge records.
The BicompRootLists and sortedDFSChildLists are of size N and start out empty.
The stack, initially empty, is made big enough for a pair of integers
per edge record (2 * arcCapacity), or 6N integers if the arcCapacity
was set below the default value.
The edgeHoles stack, initially empty, is set to arcCapacity / 2,
which is big enough to push every edge (to indicate an edge
you only need to indicate one of its two edge records)
Returns OK on success, NOTOK on all failures.
On NOTOK, graph extensions are freed so that the graph is
returned to the post-condition of gp_New().
********************************************************************/
int gp_InitGraph(graphP theGraph, int N)
{
// valid params check
if (theGraph == NULL || N <= 0)
return NOTOK;
// Should not call init a second time; use reinit
if (theGraph->N)
return NOTOK;
return theGraph->functions.fpInitGraph(theGraph, N);
}
int _InitGraph(graphP theGraph, int N)
{
int Vsize, VIsize, Esize, stackSize;
// Compute the vertex and edge capacities of the graph
theGraph->N = N;
theGraph->NV = N;
theGraph->arcCapacity = theGraph->arcCapacity > 0 ? theGraph->arcCapacity : 2*DEFAULT_EDGE_LIMIT*N;
VIsize = gp_PrimaryVertexIndexBound(theGraph);
Vsize = gp_VertexIndexBound(theGraph);
Esize = gp_EdgeIndexBound(theGraph);
// Stack size is 2 integers per arc, or 6 integers per vertex in case of small arcCapacity
stackSize = 2 * Esize;
stackSize = stackSize < 6*N ? 6*N : stackSize;
// Allocate memory as described above
if ((theGraph->V = (vertexRecP) calloc(Vsize, sizeof(vertexRec))) == NULL ||
(theGraph->VI = (vertexInfoP) calloc(VIsize, sizeof(vertexInfo))) == NULL ||
(theGraph->E = (edgeRecP) calloc(Esize, sizeof(edgeRec))) == NULL ||
(theGraph->BicompRootLists = LCNew(VIsize)) == NULL ||
(theGraph->sortedDFSChildLists = LCNew(VIsize)) == NULL ||
(theGraph->theStack = sp_New(stackSize)) == NULL ||
(theGraph->extFace = (extFaceLinkRecP) calloc(Vsize, sizeof(extFaceLinkRec))) == NULL ||
(theGraph->edgeHoles = sp_New(Esize / 2)) == NULL ||
0)
{
_ClearGraph(theGraph);
return NOTOK;
}
// Initialize memory
_InitVertices(theGraph);
_InitEdges(theGraph);
_InitIsolatorContext(theGraph);
return OK;
}
/********************************************************************
_InitVertices()
********************************************************************/
void _InitVertices(graphP theGraph)
{
#if NIL == 0
memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec));
memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo));
memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec));
#elif NIL == -1
int v;
memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec));
memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo));
memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec));
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
gp_InitVertexFlags(theGraph, v);
#else
int v;
// Initialize primary vertices
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
_InitVertexRec(theGraph, v);
_InitVertexInfo(theGraph, v);
gp_SetExtFaceVertex(theGraph, v, 0, NIL);
gp_SetExtFaceVertex(theGraph, v, 1, NIL);
}
// Initialize virtual vertices
for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++)
{
_InitVertexRec(theGraph, v);
gp_SetExtFaceVertex(theGraph, v, 0, NIL);
gp_SetExtFaceVertex(theGraph, v, 1, NIL);
}
#endif
}
/********************************************************************
_InitEdges()
********************************************************************/
void _InitEdges(graphP theGraph)
{
#if NIL == 0
memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec));
#elif NIL == -1
int e, Esize;
memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec));
Esize = gp_EdgeIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < Esize; e++)
gp_InitEdgeFlags(theGraph, e);
#else
int e, Esize;
Esize = gp_EdgeIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < Esize; e++)
_InitEdgeRec(theGraph, e);
#endif
}
/********************************************************************
gp_ReinitializeGraph()
Reinitializes a graph, restoring it to the state it was in immediately
after gp_InitGraph() processed it.
********************************************************************/
void gp_ReinitializeGraph(graphP theGraph)
{
if (theGraph == NULL || theGraph->N <= 0)
return;
theGraph->functions.fpReinitializeGraph(theGraph);
}
void _ReinitializeGraph(graphP theGraph)
{
theGraph->M = 0;
theGraph->internalFlags = theGraph->embedFlags = 0;
_InitVertices(theGraph);
_InitEdges(theGraph);
_InitIsolatorContext(theGraph);
LCReset(theGraph->BicompRootLists);
LCReset(theGraph->sortedDFSChildLists);
sp_ClearStack(theGraph->theStack);
sp_ClearStack(theGraph->edgeHoles);
}
/********************************************************************
gp_GetArcCapacity()
Returns the arcCapacity of theGraph, which is twice the maximum
number of edges that can be added to the theGraph.
********************************************************************/
int gp_GetArcCapacity(graphP theGraph)
{
return theGraph->arcCapacity - gp_GetFirstEdge(theGraph);
}
/********************************************************************
gp_EnsureArcCapacity()
This method ensures that theGraph is or will be capable of storing
at least requiredArcCapacity edge records. Two edge records are
needed per edge.
This method is most performant when invoked immediately after
gp_New(), since it must only set the arcCapacity and then let
normal initialization to occur through gp_InitGraph().
This method is also a constant time operation if the graph already
has at least the requiredArcCapacity, since it will return OK
without making any structural changes.
This method is generally more performant if it is invoked before
attaching extensions to the graph. Some extensions associate
parallel data with edge records, which is a faster operation if
the associated data is created and initialized only after the
proper arcCapacity is specified.
If the graph has been initialized and has a lower arc capacity,
then the array of edge records is reallocated to satisfy the
requiredArcCapacity. The new array contains the old edges and
edge holes at the same locations, and all newly created edge records
are initialized.
Also, if the arc capacity must be increased, then the
arcCapacity member of theGraph is changed and both
theStack and edgeHoles are expanded (since the sizes of both
are based on the arc capacity).
Extensions that add to data associated with edges must overload
this method to ensure capacity in the parallel extension data
structures. An extension can return NOTOK if it does not
support arc capacity expansion. The extension function will
not be called if arcCapacity is expanded before the graph is
initialized, and it is assumed that extensions will allocate
parallel data structures according to the arc capacity.
If an extension supports arc capacity expansion, then higher
performance can be obtained by using the method of unhooking
the initializers for individual edge records before invoking
the superclass version of fpEnsureArcCapacity(). Ideally,
application authors should ensure the proper arc capacity before
attaching extensions to achieve better performance.
Returns NOTOK on failure to reallocate the edge record array to
satisfy the requiredArcCapacity, or if the requested
capacity is odd
OK if reallocation is not required or if reallocation succeeds
********************************************************************/
int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)
{
if (theGraph == NULL || requiredArcCapacity <= 0)
return NOTOK;
// Train callers to only ask for an even number of arcs, since
// two are required per edge or directed edge.
if (requiredArcCapacity & 1)
return NOTOK;
if (theGraph->arcCapacity >= requiredArcCapacity)
return OK;
// In the special case where gp_InitGraph() has not yet been called,
// we can simply set the higher arcCapacity since normal initialization
// will then allocate the correct number of edge records.
if (theGraph->N == 0)
{
theGraph->arcCapacity = requiredArcCapacity;
return OK;
}
// Try to expand the arc capacity
return theGraph->functions.fpEnsureArcCapacity(theGraph, requiredArcCapacity);
}
int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)
{
stackP newStack;
int e, Esize = gp_EdgeIndexBound(theGraph),
newEsize = gp_GetFirstEdge(theGraph) + requiredArcCapacity;
// If the new size is less than or equal to the old size, then
// the graph already has the required arc capacity
if (newEsize <= Esize)
return OK;
// Expand theStack
if (sp_GetCapacity(theGraph->theStack) < 2 * requiredArcCapacity)
{
int stackSize = 2 * requiredArcCapacity;
if (stackSize < 6*theGraph->N)
{
// NOTE: Since this routine only makes the stack bigger, this
// calculation is not needed here because we already ensured
// we had stack capacity of the greater of 2*arcs and 6*N
// But we do it for clarity and consistency (e.g. so this rule
// is not forgotten whenever a "SetArcCapacity" method or a
// "reduceArcCapacity" method is added)
stackSize = 6*theGraph->N;
}
if ((newStack = sp_New(stackSize)) == NULL)
return NOTOK;
sp_CopyContent(newStack, theGraph->theStack);
sp_Free(&theGraph->theStack);
theGraph->theStack = newStack;
}
// Expand edgeHoles
if ((newStack = sp_New(requiredArcCapacity / 2)) == NULL)
return NOTOK;
sp_CopyContent(newStack, theGraph->edgeHoles);
sp_Free(&theGraph->edgeHoles);
theGraph->edgeHoles = newStack;
// Reallocate the edgeRec array to the new size,
theGraph->E = (edgeRecP) realloc(theGraph->E, newEsize*sizeof(edgeRec));
if (theGraph->E == NULL)
return NOTOK;
// Initialize the new edge records
for (e = Esize; e < newEsize; e++)
_InitEdgeRec(theGraph, e);
// The new arcCapacity has been successfully achieved
theGraph->arcCapacity = requiredArcCapacity;
return OK;
}
/********************************************************************
_InitVertexRec()
Sets the fields in a single vertex record to initial values
********************************************************************/
void _InitVertexRec(graphP theGraph, int v)
{
gp_SetFirstArc(theGraph, v, NIL);
gp_SetLastArc(theGraph, v, NIL);
gp_SetVertexIndex(theGraph, v, NIL);
gp_InitVertexFlags(theGraph, v);
}
/********************************************************************
_InitVertexInfo()
Sets the fields in a single vertex record to initial values
********************************************************************/
void _InitVertexInfo(graphP theGraph, int v)
{
gp_SetVertexParent(theGraph, v, NIL);
gp_SetVertexLeastAncestor(theGraph, v, NIL);
gp_SetVertexLowpoint(theGraph, v, NIL);
gp_SetVertexVisitedInfo(theGraph, v, NIL);
gp_SetVertexPertinentEdge(theGraph, v, NIL);
gp_SetVertexPertinentRootsList(theGraph, v, NIL);
gp_SetVertexFuturePertinentChild(theGraph, v, NIL);
gp_SetVertexSortedDFSChildList(theGraph, v, NIL);
gp_SetVertexFwdArcList(theGraph, v, NIL);
}
/********************************************************************
_InitEdgeRec()
Sets the fields in a single edge record structure to initial values
********************************************************************/
void _InitEdgeRec(graphP theGraph, int e)
{
gp_SetNeighbor(theGraph, e, NIL);
gp_SetPrevArc(theGraph, e, NIL);
gp_SetNextArc(theGraph, e, NIL);
gp_InitEdgeFlags(theGraph, e);
}
/********************************************************************
_InitIsolatorContext()
********************************************************************/
void _InitIsolatorContext(graphP theGraph)
{
isolatorContextP IC = &theGraph->IC;
IC->minorType = 0;
IC->v = IC->r = IC->x = IC->y = IC->w = IC->px = IC->py = IC->z =
IC->ux = IC->dx = IC->uy = IC->dy = IC->dw = IC->uz = IC->dz = NIL;
}
/********************************************************************
_ClearVisitedFlags()
********************************************************************/
void _ClearVisitedFlags(graphP theGraph)
{
_ClearVertexVisitedFlags(theGraph, TRUE);
_ClearEdgeVisitedFlags(theGraph);
}
/********************************************************************
_ClearVertexVisitedFlags()
********************************************************************/
void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices)
{
int v;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
gp_ClearVertexVisited(theGraph, v);
if (includeVirtualVertices)
for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++)
gp_ClearVertexVisited(theGraph, v);
}
/********************************************************************
_ClearEdgeVisitedFlags()
********************************************************************/
void _ClearEdgeVisitedFlags(graphP theGraph)
{
int e, EsizeOccupied;
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++)
gp_ClearEdgeVisited(theGraph, e);
}
/********************************************************************
_ClearVisitedFlagsInBicomp()
Clears the visited flag of the vertices and arcs in the bicomp rooted
by BicompRoot.
This method uses the stack but preserves whatever may have been
on it. In debug mode, it will return NOTOK if the stack overflows.
This method pushes at most one integer per vertex in the bicomp.
Returns OK on success, NOTOK on implementation failure.
********************************************************************/
int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot)
{
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
int v, e;
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, v);
gp_ClearVertexVisited(theGraph, v);
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
gp_ClearEdgeVisited(theGraph, e);
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
}
return OK;
}
/********************************************************************
_ClearVisitedFlagsInOtherBicomps()
Typically, we want to clear all visited flags in the graph
(see _ClearVisitedFlags). However, in some algorithms this would be
too costly, so it is necessary to clear the visited flags only
in one bicomp (see _ClearVisitedFlagsInBicomp), then do some processing
that sets some of the flags then performs some tests. If the tests
are positive, then we can clear all the visited flags in the
other bicomps (the processing may have set the visited flags in the
one bicomp in a particular way that we want to retain, so we skip
the given bicomp).
********************************************************************/
int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot)
{
int R;
for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++)
{
if (R != BicompRoot && gp_VirtualVertexInUse(theGraph, R))
{
if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK)
return NOTOK;
}
}
return OK;
}
/********************************************************************
_ClearVisitedFlagsInUnembeddedEdges()
Unembedded edges aren't part of any bicomp yet, but it may be
necessary to clear their visited flags.
********************************************************************/
void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph)
{
int v, e;
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
{
e = gp_GetVertexFwdArcList(theGraph, v);
while (gp_IsArc(e))
{
gp_ClearEdgeVisited(theGraph, e);
gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e));
e = gp_GetNextArc(theGraph, e);
if (e == gp_GetVertexFwdArcList(theGraph, v))
e = NIL;
}
}
}
/****************************************************************************
_ClearVisitedFlagsOnPath()
This method clears the visited flags on the vertices and edges on the path
(u, v, ..., w, x) in which all vertices except the endpoints u and x
are degree 2. This method avoids performing more than constant work at the
path endpoints u and x, so the total work is on the order of the path length.
Returns OK on success, NOTOK on internal failure
****************************************************************************/
int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x)
{
int e, eTwin;
// We want to exit u from e, but we get eTwin first here in order to avoid
// work, in case the degree of u is greater than 2.
eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u);
if (gp_IsNotArc(eTwin))
return NOTOK;
e = gp_GetTwinArc(theGraph, eTwin);
v = u;
do {
// Mark the vertex and the exiting edge
gp_ClearVertexVisited(theGraph, v);
gp_ClearEdgeVisited(theGraph, e);
gp_ClearEdgeVisited(theGraph, eTwin);
// Get the next vertex
v = gp_GetNeighbor(theGraph, e);
e = gp_GetNextArcCircular(theGraph, eTwin);
eTwin = gp_GetTwinArc(theGraph, e);
} while (v != x);
// Mark the last vertex with 'visited'
gp_ClearVertexVisited(theGraph, x);
return OK;
}
/****************************************************************************
_SetVisitedFlagsOnPath()
This method sets the visited flags on the vertices and edges on the path
(u, v, ..., w, x) in which all vertices except the endpoints u and x
are degree 2. This method avoids performing more than constant work at the
path endpoints u and x, so the total work is on the order of the path length.
Returns OK on success, NOTOK on internal failure
****************************************************************************/
int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x)
{
int e, eTwin;
// We want to exit u from e, but we get eTwin first here in order to avoid
// work, in case the degree of u is greater than 2.
eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u);
if (gp_IsNotArc(eTwin))
return NOTOK;
e = gp_GetTwinArc(theGraph, eTwin);
v = u;
do {
// Mark the vertex and the exiting edge
gp_SetVertexVisited(theGraph, v);
gp_SetEdgeVisited(theGraph, e);
gp_SetEdgeVisited(theGraph, eTwin);
// Get the next vertex
v = gp_GetNeighbor(theGraph, e);
e = gp_GetNextArcCircular(theGraph, eTwin);
eTwin = gp_GetTwinArc(theGraph, e);
} while (v != x);
// Mark the last vertex with 'visited'
gp_SetVertexVisited(theGraph, x);
return OK;
}
/********************************************************************
_FillVertexVisitedInfoInBicomp()
Places the FillValue into the visitedInfo of the non-virtual vertices
in the bicomp rooted by BicompRoot.
This method uses the stack but preserves whatever may have been
on it. In debug mode, it will return NOTOK if the stack overflows.
This method pushes at most one integer per vertex in the bicomp.
Returns OK on success, NOTOK on implementation failure.
********************************************************************/
int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue)
{
int v, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, v);
if (gp_IsNotVirtualVertex(theGraph, v))
gp_SetVertexVisitedInfo(theGraph, v, FillValue);
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
}
return OK;
}
/********************************************************************
_ClearVertexTypeInBicomp()
Clears the 'obstruction type' bits for each vertex in the bicomp
rooted by BicompRoot.
This method uses the stack but preserves whatever may have been
on it. In debug mode, it will return NOTOK if the stack overflows.
This method pushes at most one integer per vertex in the bicomp.
Returns OK on success, NOTOK on implementation failure.
********************************************************************/
int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot)
{
int V, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
gp_ClearVertexObstructionType(theGraph, V);
e = gp_GetFirstArc(theGraph, V);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
}
return OK;
}
/********************************************************************
_ClearGraph()
Clears all memory used by the graph, restoring it to the state it
was in immediately after gp_New() created it.
********************************************************************/
void _ClearGraph(graphP theGraph)
{
if (theGraph->V != NULL)
{
free(theGraph->V);
theGraph->V = NULL;
}
if (theGraph->VI != NULL)
{
free(theGraph->VI);
theGraph->V = NULL;
}
if (theGraph->E != NULL)
{
free(theGraph->E);
theGraph->E = NULL;
}
theGraph->N = 0;
theGraph->NV = 0;
theGraph->M = 0;
theGraph->arcCapacity = 0;
theGraph->internalFlags = 0;
theGraph->embedFlags = 0;
_InitIsolatorContext(theGraph);
LCFree(&theGraph->BicompRootLists);
LCFree(&theGraph->sortedDFSChildLists);
sp_Free(&theGraph->theStack);
if (theGraph->extFace != NULL)
{
free(theGraph->extFace);
theGraph->extFace = NULL;
}
sp_Free(&theGraph->edgeHoles);
gp_FreeExtensions(theGraph);
}
/********************************************************************
gp_Free()
Frees G and V, then the graph record. Then sets your pointer to NULL
(so you must pass the address of your pointer).
********************************************************************/
void gp_Free(graphP *pGraph)
{
if (pGraph == NULL) return;
if (*pGraph == NULL) return;
_ClearGraph(*pGraph);
free(*pGraph);
*pGraph = NULL;
}
/********************************************************************
gp_CopyAdjacencyLists()
Copies the adjacency lists from the srcGraph to the dstGraph.
This method intentionally copies only the adjacency lists of the
first N vertices, so the adjacency lists of virtual vertices are
excluded (unless the caller temporarily resets the value of N to
include NV).
Returns OK on success, NOTOK on failures, e.g. if the two graphs
have different orders N or if the arcCapacity of dstGraph cannot
be increased to match that of srcGraph.
********************************************************************/
int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph)
{
int v, e, EsizeOccupied;
if (dstGraph == NULL || srcGraph == NULL)
return NOTOK;
if (dstGraph->N != srcGraph->N || dstGraph->N == 0)
return NOTOK;
if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK)
return NOTOK;
// Copy the links that hook each owning vertex to its adjacency list
for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++)
{
gp_SetFirstArc(dstGraph, v, gp_GetFirstArc(srcGraph, v));
gp_SetLastArc(dstGraph, v, gp_GetLastArc(srcGraph, v));
}
// Copy the adjacency links and neighbor pointers for each arc
EsizeOccupied = gp_EdgeInUseIndexBound(srcGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++)
{
gp_SetNeighbor(dstGraph, e, gp_GetNeighbor(srcGraph, e));
gp_SetNextArc(dstGraph, e, gp_GetNextArc(srcGraph, e));
gp_SetPrevArc(dstGraph, e, gp_GetPrevArc(srcGraph, e));
}
// Tell the dstGraph how many edges it now has and where the edge holes are
dstGraph->M = srcGraph->M;
sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles);
return OK;
}
/********************************************************************
gp_CopyGraph()
Copies the content of the srcGraph into the dstGraph. The dstGraph
must have been previously initialized with the same number of
vertices as the srcGraph (e.g. gp_InitGraph(dstGraph, srcGraph->N).
Returns OK for success, NOTOK for failure.
********************************************************************/
int gp_CopyGraph(graphP dstGraph, graphP srcGraph)
{
int v, e, Esize;
// Parameter checks
if (dstGraph == NULL || srcGraph == NULL)
{
return NOTOK;
}
// The graphs need to be the same order and initialized
if (dstGraph->N != srcGraph->N || dstGraph->N == 0)
{
return NOTOK;
}
// Ensure dstGraph has the required arc capacity; this expands
// dstGraph if needed, but does not contract. An error is only
// returned if the expansion fails.
if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK)
{
return NOTOK;
}
// Copy the primary vertices. Augmentations to vertices created
// by extensions are copied below by gp_CopyExtensions()
for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++)
{
gp_CopyVertexRec(dstGraph, v, srcGraph, v);
gp_CopyVertexInfo(dstGraph, v, srcGraph, v);
gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0));
gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1));
}
// Copy the virtual vertices. Augmentations to virtual vertices created
// by extensions are copied below by gp_CopyExtensions()
for (v = gp_GetFirstVirtualVertex(srcGraph); gp_VirtualVertexInRange(srcGraph, v); v++)
{
gp_CopyVertexRec(dstGraph, v, srcGraph, v);
gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0));
gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1));
}
// Copy the basic EdgeRec structures. Augmentations to the edgeRec structure
// created by extensions are copied below by gp_CopyExtensions()
Esize = gp_EdgeIndexBound(srcGraph);
for (e = gp_GetFirstEdge(theGraph); e < Esize; e++)
gp_CopyEdgeRec(dstGraph, e, srcGraph, e);
// Give the dstGraph the same size and intrinsic properties
dstGraph->N = srcGraph->N;
dstGraph->NV = srcGraph->NV;
dstGraph->M = srcGraph->M;
dstGraph->internalFlags = srcGraph->internalFlags;
dstGraph->embedFlags = srcGraph->embedFlags;
dstGraph->IC = srcGraph->IC;
LCCopy(dstGraph->BicompRootLists, srcGraph->BicompRootLists);
LCCopy(dstGraph->sortedDFSChildLists, srcGraph->sortedDFSChildLists);
sp_Copy(dstGraph->theStack, srcGraph->theStack);
sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles);
// Copy the set of extensions, which includes copying the
// extension data as well as the function overload tables
if (gp_CopyExtensions(dstGraph, srcGraph) != OK)
return NOTOK;
// Copy the graph's function table, which has the pointers to
// the most recent extension overloads of each function (or
// the original function pointer if a particular function has
// not been overloaded).
// This must be done after copying the extension because the
// first step of copying the extensions is to delete the
// dstGraph extensions, which clears its function table.
// Therefore, no good to assign the srcGraph functions *before*
// copying the extensions because the assignment would be wiped out
// This, in turn, means that the DupContext function of an extension
// *cannot* depend on any extension function overloads; the extension
// must directly invoke extension functions only.
dstGraph->functions = srcGraph->functions;
return OK;
}
/********************************************************************
gp_DupGraph()
********************************************************************/
graphP gp_DupGraph(graphP theGraph)
{
graphP result;
if ((result = gp_New()) == NULL) return NULL;
if (gp_InitGraph(result, theGraph->N) != OK ||
gp_CopyGraph(result, theGraph) != OK)
{
gp_Free(&result);
return NULL;
}
return result;
}
/********************************************************************
gp_CreateRandomGraph()
Creates a randomly generated graph. First a tree is created by
connecting each vertex to some successor. Then a random number of
additional random edges are added. If an edge already exists, then
we retry until a non-existent edge is picked.
This function assumes the caller has already called srand().
Returns OK on success, NOTOK on failure
********************************************************************/
int gp_CreateRandomGraph(graphP theGraph)
{
int N, M, u, v, m;
N = theGraph->N;
/* Generate a random tree; note that this method virtually guarantees
that the graph will be renumbered, but it is linear time.
Also, we are not generating the DFS tree but rather a tree
that simply ensures the resulting random graph is connected. */
for (v = gp_GetFirstVertex(theGraph)+1; gp_VertexInRange(theGraph, v); v++)
{
u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v-1);
if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
return NOTOK;
}
/* Generate a random number of additional edges
(actually, leave open a small chance that no
additional edges will be added). */
M = _GetRandomNumber(7*N/8, theGraph->arcCapacity/2);
if (M > N*(N-1)/2)
M = N*(N-1)/2;
for (m = N-1; m < M; m++)
{
u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)-1);
v = _GetRandomNumber(u+1, gp_GetLastVertex(theGraph));
// If the edge (u,v) exists, decrement eIndex to try again
if (gp_IsNeighbor(theGraph, u, v))
m--;
// If the edge (u,v) doesn't exist, add it
else
{
if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
return NOTOK;
}
}
return OK;
}
/********************************************************************
_GetRandomNumber()
This function generates a random number between NMin and NMax
inclusive. It assumes that the caller has called srand().
It calls rand(), but before truncating to the proper range,
it adds the high bits of the rand() result into the low bits.
The result of this is that the randomness appearing in the
truncated bits also has an affect on the non-truncated bits.
********************************************************************/
int _GetRandomNumber(int NMin, int NMax)
{
int N = rand();
if (NMax < NMin) return NMin;
N += ((N&0xFFFF0000)>>16);
N += ((N&0x0000FF00)>>8);
N &= 0x7FFFFFF;
N %= (NMax-NMin+1);
return N+NMin;
}
/********************************************************************
_getUnprocessedChild()
Support routine for gp_Create RandomGraphEx(), this function
obtains a child of the given vertex in the randomly generated
tree that has not yet been processed. NIL is returned if the
given vertex has no unprocessed children
********************************************************************/
int _getUnprocessedChild(graphP theGraph, int parent)
{
int e = gp_GetFirstArc(theGraph, parent);
int eTwin = gp_GetTwinArc(theGraph, e);
int child = gp_GetNeighbor(theGraph, e);
// The tree edges were added to the beginning of the adjacency list,
// and we move processed tree edge records to the end of the list,
// so if the immediate next arc (edge record) is not a tree edge
// then we return NIL because the vertex has no remaining
// unprocessed children
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED)
return NIL;
// If the child has already been processed, then all children
// have been pushed to the end of the list, and we have just
// encountered the first child we processed, so there are no
// remaining unprocessed children */
if (gp_GetEdgeVisited(theGraph, e))
return NIL;
// We have found an edge leading to an unprocessed child, so
// we mark it as processed so that it doesn't get returned
// again in future iterations.
gp_SetEdgeVisited(theGraph, e);
gp_SetEdgeVisited(theGraph, eTwin);
// Now we move the edge record in the parent vertex to the end
// of the adjacency list of that vertex.
gp_MoveArcToLast(theGraph, parent, e);
// Now we move the edge record in the child vertex to the
// end of the adjacency list of the child.
gp_MoveArcToLast(theGraph, child, eTwin);
// Now we set the child's parent and return the child.
gp_SetVertexParent(theGraph, child, parent);
return child;
}
/********************************************************************
_hasUnprocessedChild()
Support routine for gp_Create RandomGraphEx(), this function
obtains a child of the given vertex in the randomly generated
tree that has not yet been processed. False (0) is returned
unless the given vertex has an unprocessed child.
********************************************************************/
int _hasUnprocessedChild(graphP theGraph, int parent)
{
int e = gp_GetFirstArc(theGraph, parent);
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED)
return 0;
if (gp_GetEdgeVisited(theGraph, e))
return 0;
return 1;
}
/********************************************************************
gp_CreateRandomGraphEx()
Given a graph structure with a pre-specified number of vertices N,
this function creates a graph with the specified number of edges.
If numEdges <= 3N-6, then the graph generated is planar. If
numEdges is larger, then a maximal planar graph is generated, then
(numEdges - 3N + 6) additional random edges are added.
This function assumes the caller has already called srand().
********************************************************************/
int gp_CreateRandomGraphEx(graphP theGraph, int numEdges)
{
int N, arc, M, root, v, c, p, last, u, e, EsizeOccupied;
N = theGraph->N;
if (numEdges > theGraph->arcCapacity/2)
numEdges = theGraph->arcCapacity/2;
/* Generate a random tree. */
for (v = gp_GetFirstVertex(theGraph)+1; gp_VertexInRange(theGraph, v); v++)
{
u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v-1);
if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
return NOTOK;
else
{
arc = 2*theGraph->M - 2;
gp_SetEdgeType(theGraph, arc, EDGE_TYPE_RANDOMTREE);
gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, arc), EDGE_TYPE_RANDOMTREE);
gp_ClearEdgeVisited(theGraph, arc);
gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, arc));
}
}
/* Add edges up to the limit or until the graph is maximal planar. */
M = numEdges <= 3*N - 6 ? numEdges : 3*N - 6;
root = 0;
v = last = _getUnprocessedChild(theGraph, root);
while (v != root && theGraph->M < M)
{
c = _getUnprocessedChild(theGraph, v);
if (gp_IsVertex(c))
{
if (last != v)
{
if (gp_AddEdge(theGraph, last, 1, c, 1) != OK)
return NOTOK;
}
if (gp_AddEdge(theGraph, root, 1, c, 1) != OK)
return NOTOK;
v = last = c;
}
else
{
p = gp_GetVertexParent(theGraph, v);
while (gp_IsVertex(p) && gp_IsNotVertex(c = _getUnprocessedChild(theGraph, p)))
{
v = p;
p = gp_GetVertexParent(theGraph, v);
if (gp_IsVertex(p) && p != root)
{
if (gp_AddEdge(theGraph, last, 1, p, 1) != OK)
return NOTOK;
}
}
if (gp_IsVertex(p))
{
if (p == root)
{
if (gp_AddEdge(theGraph, v, 1, c, 1) != OK)
return NOTOK;
if (v != last)
{
if (gp_AddEdge(theGraph, last, 1, c, 1) != OK)
return NOTOK;
}
}
else
{
if (gp_AddEdge(theGraph, last, 1, c, 1) != OK)
return NOTOK;
}
if (p != root)
{
if (gp_AddEdge(theGraph, root, 1, c, 1) != OK)
return NOTOK;
last = c;
}
v = c;
}
}
}
/* Add additional edges if the limit has not yet been reached. */
while (theGraph->M < numEdges)
{
u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph));
v = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph));
if (u != v && !gp_IsNeighbor(theGraph, u, v))
if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
return NOTOK;
}
/* Clear the edge types back to 'unknown' */
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = 0; e < EsizeOccupied; e++)
{
gp_ClearEdgeType(theGraph, e);
gp_ClearEdgeVisited(theGraph, e);
}
/* Put all DFSParent indicators back to NIL */
for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++)
gp_SetVertexParent(theGraph, v, NIL);
return OK;
}
/********************************************************************
gp_IsNeighbor()
Checks whether v is already in u's adjacency list, i.e. does the arc
u -> v exist.
If there is an edge record for v in u's list, but it is marked INONLY,
then it represents the arc v->u but not u->v, so it is ignored.
Returns TRUE or FALSE.
********************************************************************/
int gp_IsNeighbor(graphP theGraph, int u, int v)
{
int e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
if (gp_GetNeighbor(theGraph, e) == v)
{
if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY)
return TRUE;
}
e = gp_GetNextArc(theGraph, e);
}
return FALSE;
}
/********************************************************************
gp_GetNeighborEdgeRecord()
Searches the adjacency list of u to obtains the edge record for v.
NOTE: The caller should check whether the edge record is INONLY;
This method returns any edge record representing a connection
between vertices u and v, so this method can return an
edge record even if gp_IsNeighbor(theGraph, u, v) is false (0).
To filter out INONLY edge records, use gp_GetDirection() on
the edge record returned by this method.
Returns NIL if there is no edge record indicating v in u's adjacency
list, or the edge record location otherwise.
********************************************************************/
int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v)
{
int e;
if (gp_IsNotVertex(u) || gp_IsNotVertex(v))
return NIL + NOTOK - NOTOK;
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
if (gp_GetNeighbor(theGraph, e) == v)
return e;
e = gp_GetNextArc(theGraph, e);
}
return NIL;
}
/********************************************************************
gp_GetVertexDegree()
Counts the number of edge records in the adjacency list of a given
vertex V.
Note: For digraphs, this method returns the total degree of the
vertex, including outward arcs (undirected and OUTONLY)
as well as INONLY arcs. Other functions are defined to get
the in-degree or out-degree of the vertex.
Note: This function determines the degree by counting. An extension
could cache the degree value of each vertex and update the
cached value as edges are added and deleted.
********************************************************************/
int gp_GetVertexDegree(graphP theGraph, int v)
{
int e, degree;
if (theGraph==NULL || gp_IsNotVertex(v))
return 0 + NOTOK - NOTOK;
degree = 0;
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
degree++;
e = gp_GetNextArc(theGraph, e);
}
return degree;
}
/********************************************************************
gp_GetVertexInDegree()
Counts the number of edge records in the adjacency list of a given
vertex V that represent arcs from another vertex into V.
This includes undirected edges and INONLY arcs, so it only excludes
edges records that are marked as OUTONLY arcs.
Note: This function determines the in-degree by counting. An extension
could cache the in-degree value of each vertex and update the
cached value as edges are added and deleted.
********************************************************************/
int gp_GetVertexInDegree(graphP theGraph, int v)
{
int e, degree;
if (theGraph==NULL || gp_IsNotVertex(v))
return 0 + NOTOK - NOTOK;
degree = 0;
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_OUTONLY)
degree++;
e = gp_GetNextArc(theGraph, e);
}
return degree;
}
/********************************************************************
gp_GetVertexOutDegree()
Counts the number of edge records in the adjacency list of a given
vertex V that represent arcs from V to another vertex.
This includes undirected edges and OUTONLY arcs, so it only excludes
edges records that are marked as INONLY arcs.
Note: This function determines the out-degree by counting. An extension
could cache the out-degree value of each vertex and update the
cached value as edges are added and deleted.
********************************************************************/
int gp_GetVertexOutDegree(graphP theGraph, int v)
{
int e, degree;
if (theGraph==NULL || gp_IsNotVertex(v))
return 0 + NOTOK - NOTOK;
degree = 0;
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY)
degree++;
e = gp_GetNextArc(theGraph, e);
}
return degree;
}
/********************************************************************
gp_AttachArc()
This routine adds newArc into v's adjacency list at a position
adjacent to the edge record for e, either before or after e,
depending on link. If e is not an arc (e.g. if e is NIL),
then link is assumed to indicate whether the new arc is to be
placed at the beginning or end of v's adjacency list.
NOTE: The caller can pass NIL for v if e is not NIL, since the
vertex is implied (gp_GetNeighbor(theGraph, eTwin))
The arc is assumed to already exist in the data structure (i.e.
the storage of edges), as only a whole edge (two arcs) can be
inserted into or deleted from the data structure. Hence there is
no such thing as gp_InsertArc() or gp_DeleteArc().
See also gp_DetachArc(), gp_InsertEdge() and gp_DeleteEdge()
********************************************************************/
void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc)
{
if (gp_IsArc(e))
{
int e2 = gp_GetAdjacentArc(theGraph, e, link);
// e's link is newArc, and newArc's 1^link is e
gp_SetAdjacentArc(theGraph, e, link, newArc);
gp_SetAdjacentArc(theGraph, newArc, 1^link, e);
// newArcs's link is e2
gp_SetAdjacentArc(theGraph, newArc, link, e2);
// if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc
if (gp_IsArc(e2))
gp_SetAdjacentArc(theGraph, e2, 1^link, newArc);
else
gp_SetArc(theGraph, v, 1^link, newArc);
}
else
{
int e2 = gp_GetArc(theGraph, v, link);
// v's link is newArc, and newArc's 1^link is NIL
gp_SetArc(theGraph, v, link, newArc);
gp_SetAdjacentArc(theGraph, newArc, 1^link, NIL);
// newArcs's elink is e2
gp_SetAdjacentArc(theGraph, newArc, link, e2);
// if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc
if (gp_IsArc(e2))
gp_SetAdjacentArc(theGraph, e2, 1^link, newArc);
else
gp_SetArc(theGraph, v, 1^link, newArc);
}
}
/****************************************************************************
gp_DetachArc()
This routine detaches arc from its adjacency list, but it does not delete
it from the data structure (only a whole edge can be deleted).
Some algorithms must temporarily detach an edge, perform some calculation,
and eventually put the edge back. This routine supports that operation.
The neighboring adjacency list nodes are cross-linked, but the two link
members of the arc are retained, so the arc can be reattached later by
invoking _RestoreArc(). A sequence of detached arcs can only be restored
in the exact opposite order of their detachment. Thus, algorithms do not
directly use this method to implement the temporary detach/restore method.
Instead, gp_HideEdge() and gp_RestoreEdge are used, and algorithms push
edge hidden edge onto the stack. One example of this stack usage is
provided by detaching edges with gp_ContractEdge() or gp_IdentifyVertices(),
and reattaching with gp_RestoreIdentifications(), which unwinds the stack
by invoking gp_RestoreVertex().
****************************************************************************/
void gp_DetachArc(graphP theGraph, int arc)
{
int nextArc = gp_GetNextArc(theGraph, arc),
prevArc = gp_GetPrevArc(theGraph, arc);
if (gp_IsArc(nextArc))
gp_SetPrevArc(theGraph, nextArc, prevArc);
else
gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), prevArc);
if (gp_IsArc(prevArc))
gp_SetNextArc(theGraph, prevArc, nextArc);
else
gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), nextArc);
}
/********************************************************************
gp_AddEdge()
Adds the undirected edge (u,v) to the graph by placing edge records
representing u into v's circular edge record list and v into u's
circular edge record list.
upos receives the location in G where the u record in v's list will be
placed, and vpos is the location in G of the v record we placed in
u's list. These are used to initialize the short circuit links.
ulink (0|1) indicates whether the edge record to v in u's list should
become adjacent to u by its 0 or 1 link, i.e. u[ulink] == vpos.
vlink (0|1) indicates whether the edge record to u in v's list should
become adjacent to v by its 0 or 1 link, i.e. v[vlink] == upos.
********************************************************************/
int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink)
{
int upos, vpos;
if (theGraph==NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) ||
!gp_VirtualVertexInRange(theGraph, u) || !gp_VirtualVertexInRange(theGraph, v))
return NOTOK;
/* We enforce the edge limit */
if (theGraph->M >= theGraph->arcCapacity/2)
return NONEMBEDDABLE;
if (sp_NonEmpty(theGraph->edgeHoles))
{
sp_Pop(theGraph->edgeHoles, vpos);
}
else
vpos = gp_EdgeInUseIndexBound(theGraph);
upos = gp_GetTwinArc(theGraph, vpos);
gp_SetNeighbor(theGraph, upos, v);
gp_AttachArc(theGraph, u, NIL, ulink, upos);
gp_SetNeighbor(theGraph, vpos, u);
gp_AttachArc(theGraph, v, NIL, vlink, vpos);
theGraph->M++;
return OK;
}
/********************************************************************
gp_InsertEdge()
This function adds the edge (u, v) such that the edge record added
to the adjacency list of u is adjacent to e_u and the edge record
added to the adjacency list of v is adjacent to e_v.
The direction of adjacency is given by e_ulink for e_u and e_vlink
for e_v. Specifically, the new edge will be comprised of two arcs,
n_u and n_v. In u's (v's) adjacency list, n_u (n_v) will be added
so that it is indicated by e_u's (e_v's) e_ulink (e_vlink).
If e_u (or e_v) is not an arc, then e_ulink (e_vlink) indicates
whether to prepend or append to the adjacency list for u (v).
********************************************************************/
int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink,
int v, int e_v, int e_vlink)
{
int vertMax = gp_GetLastVirtualVertex(theGraph),
edgeMax = gp_EdgeInUseIndexBound(theGraph) - 1,
upos, vpos;
if (theGraph==NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) ||
u > vertMax || v > vertMax ||
e_u > edgeMax || (e_u < gp_GetFirstEdge(theGraph) && gp_IsArc(e_u)) ||
e_v > edgeMax || (e_v < gp_GetFirstEdge(theGraph) && gp_IsArc(e_v)) ||
e_ulink < 0 || e_ulink > 1 || e_vlink < 0 || e_vlink > 1)
return NOTOK;
if (theGraph->M >= theGraph->arcCapacity/2)
return NONEMBEDDABLE;
if (sp_NonEmpty(theGraph->edgeHoles))
{
sp_Pop(theGraph->edgeHoles, vpos);
}
else
vpos = gp_EdgeInUseIndexBound(theGraph);
upos = gp_GetTwinArc(theGraph, vpos);
gp_SetNeighbor(theGraph, upos, v);
gp_AttachArc(theGraph, u, e_u, e_ulink, upos);
gp_SetNeighbor(theGraph, vpos, u);
gp_AttachArc(theGraph, v, e_v, e_vlink, vpos);
theGraph->M++;
return OK;
}
/****************************************************************************
gp_DeleteEdge()
This function deletes the given edge record e and its twin, reducing the
number of edges M in the graph.
Before the e^th record is deleted, its 'nextLink' adjacency list neighbor
is collected as the return result. This is useful when iterating through
an edge list and making deletions because the nextLink arc is the 'next'
arc in the iteration, but it is hard to obtain *after* deleting e.
****************************************************************************/
int gp_DeleteEdge(graphP theGraph, int e, int nextLink)
{
// Calculate the nextArc after e so that, when e is deleted, the return result
// informs a calling loop of the next edge to be processed.
int nextArc = gp_GetAdjacentArc(theGraph, e, nextLink);
// Delete the edge records e and eTwin from their adjacency lists.
gp_DetachArc(theGraph, e);
gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e));
// Clear the two edge records
// (the bit twiddle (e & ~1) chooses the lesser of e and its twin arc)
#if NIL == 0
memset(theGraph->E + (e & ~1), NIL_CHAR, sizeof(edgeRec) << 1);
#else
_InitEdgeRec(theGraph, e);
_InitEdgeRec(theGraph, gp_GetTwinArc(theGraph, e));
#endif
// Now we reduce the number of edges in the data structure
theGraph->M--;
// If records e and eTwin were not the last in the edge record array,
// then record a new hole in the edge array. */
if (e < gp_EdgeInUseIndexBound(theGraph))
{
sp_Push(theGraph->edgeHoles, e);
}
// Return the previously calculated successor of e.
return nextArc;
}
/********************************************************************
_RestoreArc()
This routine reinserts an arc into the edge list from which it
was previously removed by gp_DetachArc().
The assumed processing model is that arcs will be restored in reverse
of the order in which they were hidden, i.e. it is assumed that the
hidden arcs will be pushed on a stack and the arcs will be popped
from the stack for restoration.
********************************************************************/
void _RestoreArc(graphP theGraph, int arc)
{
int nextArc = gp_GetNextArc(theGraph, arc),
prevArc = gp_GetPrevArc(theGraph, arc);
if (gp_IsArc(nextArc))
gp_SetPrevArc(theGraph, nextArc, arc);
else
gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc);
if (gp_IsArc(prevArc))
gp_SetNextArc(theGraph, prevArc, arc);
else
gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc);
}
/********************************************************************
gp_HideEdge()
This routine removes the two arcs of an edge from the adjacency lists
of its endpoint vertices, but does not delete them from the storage
data structure.
Many algorithms must temporarily remove an edge, perform some
calculation, and eventually put the edge back. This routine supports
that operation.
For each arc, the neighboring adjacency list nodes are cross-linked,
but the links in the arc are retained because they indicate the
neighbor arcs to which the arc can be reattached by gp_RestoreEdge().
********************************************************************/
void gp_HideEdge(graphP theGraph, int e)
{
theGraph->functions.fpHideEdge(theGraph, e);
}
void _HideEdge(graphP theGraph, int e)
{
gp_DetachArc(theGraph, e);
gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e));
}
/********************************************************************
gp_RestoreEdge()
This routine reinserts two two arcs of an edge into the adjacency
lists of the edge's endpoints, the arcs having been previously
removed by gp_HideEdge().
The assumed processing model is that edges will be restored in
reverse of the order in which they were hidden, i.e. it is assumed
that the hidden edges will be pushed on a stack and the edges will
be popped from the stack for restoration.
Note: Since both arcs of an edge are restored, only one arc need
be pushed on the stack for restoration. This routine
restores the two arcs in the opposite order from the order
in which they are hidden by gp_HideEdge().
********************************************************************/
void gp_RestoreEdge(graphP theGraph, int e)
{
theGraph->functions.fpRestoreEdge(theGraph, e);
}
void _RestoreEdge(graphP theGraph, int e)
{
_RestoreArc(theGraph, gp_GetTwinArc(theGraph, e));
_RestoreArc(theGraph, e);
}
/********************************************************************
_HideInternalEdges()
Pushes onto the graph's stack and hides all arc nodes of the vertex
except the first and last arcs in the adjacency list of the vertex.
This method is typically called on a vertex that is on the external
face of a biconnected component, because the first and last arcs are
the ones that attach the vertex to the external face cycle, and any
other arcs in the adjacency list are inside that cycle.
This method uses the stack. The caller is expected to clear the stack
or save the stack size before invocation, since the stack size is
needed to _RestoreInternalEdges().
********************************************************************/
int _HideInternalEdges(graphP theGraph, int vertex)
{
int e = gp_GetFirstArc(theGraph, vertex);
// If the vertex adjacency list is empty or if it contains
// only one edge, then there are no *internal* edges to hide
if (e == gp_GetLastArc(theGraph, vertex))
return OK;
// Start with the first internal edge
e = gp_GetNextArc(theGraph, e);
// Cycle through all the edges, pushing each except stop
// before pushing the last edge, which is not internal
while (e != gp_GetLastArc(theGraph, vertex))
{
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
e = gp_GetNextArc(theGraph, e);
}
return OK;
}
/********************************************************************
_RestoreInternalEdges()
Reverses the effects of _HideInternalEdges()
********************************************************************/
int _RestoreInternalEdges(graphP theGraph, int stackBottom)
{
return _RestoreHiddenEdges(theGraph, stackBottom);
}
/********************************************************************
_RestoreHiddenEdges()
Each entry on the stack, down to stackBottom, is assumed to be an
edge record (arc) pushed in concert with invoking gp_HideEdge().
Each edge is restored using gp_RestoreEdge() in exact reverse of the
hiding order. The stack is reduced in size to stackBottom.
Returns OK on success, NOTOK on internal failure.
********************************************************************/
int _RestoreHiddenEdges(graphP theGraph, int stackBottom)
{
int e;
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, e);
if (gp_IsNotArc(e))
return NOTOK;
gp_RestoreEdge(theGraph, e);
}
return OK;
}
/********************************************************************
gp_HideVertex()
Pushes onto the graph's stack and hides all arc nodes of the vertex.
Additional integers are then pushed so that the result is reversible
by gp_RestoreVertex(). See that method for details on the expected
stack segment.
Returns OK for success, NOTOK for internal failure.
********************************************************************/
int gp_HideVertex(graphP theGraph, int vertex)
{
if (gp_IsNotVertex(vertex))
return NOTOK;
return theGraph->functions.fpHideVertex(theGraph, vertex);
}
int _HideVertex(graphP theGraph, int vertex)
{
int hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack);
int e = gp_GetFirstArc(theGraph, vertex);
// Cycle through all the edges, pushing and hiding each
while (gp_IsArc(e))
{
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
e = gp_GetNextArc(theGraph, e);
}
// Push the additional integers needed by gp_RestoreVertex()
sp_Push(theGraph->theStack, hiddenEdgeStackBottom);
sp_Push(theGraph->theStack, NIL);
sp_Push(theGraph->theStack, NIL);
sp_Push(theGraph->theStack, NIL);
sp_Push(theGraph->theStack, NIL);
sp_Push(theGraph->theStack, NIL);
sp_Push(theGraph->theStack, vertex);
return OK;
}
/********************************************************************
gp_ContractEdge()
Contracts the edge e=(u,v). This hides the edge (both e and its
twin arc), and it also identifies vertex v with u.
See gp_IdentifyVertices() for further details.
Returns OK for success, NOTOK for internal failure.
********************************************************************/
int gp_ContractEdge(graphP theGraph, int e)
{
if (gp_IsNotArc(e))
return NOTOK;
return theGraph->functions.fpContractEdge(theGraph, e);
}
int _ContractEdge(graphP theGraph, int e)
{
int eBefore, u, v;
if (gp_IsNotArc(e))
return NOTOK;
u = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, e));
v = gp_GetNeighbor(theGraph, e);
eBefore = gp_GetNextArc(theGraph, e);
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
return gp_IdentifyVertices(theGraph, u, v, eBefore);
}
/********************************************************************
gp_IdentifyVertices()
Identifies vertex v with vertex u by transferring all adjacencies
of v to u. Any duplicate edges are removed as described below.
The non-duplicate edges of v are added to the adjacency list of u
without disturbing their relative order, and they are added before
the edge record eBefore in u's list. If eBefore is NIL, then the
edges are simply appended to u's list.
If u and v are adjacent, then gp_HideEdge() is invoked to remove
the edge e=(u,v). Then, the edges of v that indicate neighbors of
u are also hidden. This is done by setting the visited flags of
u's neighbors, then traversing the adjacency list of v. For each
visited neighbor of v, the edge is hidden because it would duplicate
an adjacency already expressed in u's list. Finally, the remaining
edges of v are moved to u's list, and each twin arc is adjusted
to indicate u as a neighbor rather than v.
This routine assumes that the visited flags are clear beforehand,
and visited flag settings made herein are cleared before returning.
The following are pushed, in order, onto the graph's built-in stack:
1) an integer for each hidden edge
2) the stack size before any hidden edges were pushed
3) six integers that indicate u, v and the edges moved from v to u
An algorithm that identifies a series of vertices, either through
directly calling this method or via gp_ContractEdge(), can unwind
the identifications using gp_RestoreIdentifications(), which
invokes gp_RestoreVertex() repeatedly.
Returns OK on success, NOTOK on internal failure
********************************************************************/
int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore)
{
return theGraph->functions.fpIdentifyVertices(theGraph, u, v, eBefore);
}
int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore)
{
int e = gp_GetNeighborEdgeRecord(theGraph, u, v);
int hiddenEdgeStackBottom, eBeforePred;
// If the vertices are adjacent, then the identification is
// essentially an edge contraction with a bit of fixup.
if (gp_IsArc(e))
{
int result = gp_ContractEdge(theGraph, e);
// The edge contraction operation pushes one hidden edge then
// recursively calls this method. This method then pushes K
// hidden edges then an integer indicating where the top of
// stack was before the edges were hidden. That integer
// indicator must be decremented, thereby incrementing the
// number of hidden edges to K+1.
// After pushing the K hidden edges and the stackBottom of
// the hidden edges, the recursive call to this method pushes
// six more integers to indicate edges that were moved from
// v to u, so the "hidden edges stackBottom" is in the next
// position down.
int hiddenEdgesStackBottomIndex = sp_GetCurrentSize(theGraph->theStack)-7;
int hiddenEdgesStackBottomValue = sp_Get(theGraph->theStack, hiddenEdgesStackBottomIndex);
sp_Set(theGraph->theStack, hiddenEdgesStackBottomIndex, hiddenEdgesStackBottomValue - 1);
return result;
}
// Now, u and v are not adjacent. Before we do any edge hiding or
// moving, we record the current stack size, as this is the
// stackBottom for the edges that will be hidden next.
hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack);
// Mark as visited all neighbors of u
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)))
return NOTOK;
gp_SetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
// For each edge record of v, if the neighbor is visited, then
// push and hide the edge.
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)))
{
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
}
e = gp_GetNextArc(theGraph, e);
}
// Mark as unvisited all neighbors of u
e = gp_GetFirstArc(theGraph, u);
while (gp_IsArc(e))
{
gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
// Push the hiddenEdgeStackBottom as a record of how many hidden
// edges were pushed (also, see above for Contract Edge adjustment)
sp_Push(theGraph->theStack, hiddenEdgeStackBottom);
// Moving v's adjacency list to u is aided by knowing the predecessor
// of u's eBefore (the edge record in u's list before which the
// edge records of v will be added).
eBeforePred = gp_IsArc(eBefore)
? gp_GetPrevArc(theGraph, eBefore)
: gp_GetLastArc(theGraph, u);
// Turns out we only need to record six integers related to the edges
// being moved in order to easily restore them later.
sp_Push(theGraph->theStack, eBefore);
sp_Push(theGraph->theStack, gp_GetLastArc(theGraph, v));
sp_Push(theGraph->theStack, gp_GetFirstArc(theGraph, v));
sp_Push(theGraph->theStack, eBeforePred);
sp_Push(theGraph->theStack, u);
sp_Push(theGraph->theStack, v);
// For the remaining edge records of v, reassign the 'v' member
// of each twin arc to indicate u rather than v.
e = gp_GetFirstArc(theGraph, v);
while (gp_IsArc(e))
{
gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), u);
e = gp_GetNextArc(theGraph, e);
}
// If v has any edges left after hiding edges, indicating common neighbors with u, ...
if (gp_IsArc(gp_GetFirstArc(theGraph, v)))
{
// Then perform the list union of v into u between eBeforePred and eBefore
if (gp_IsArc(eBeforePred))
{
if (gp_IsArc(gp_GetFirstArc(theGraph, v)))
{
gp_SetNextArc(theGraph, eBeforePred, gp_GetFirstArc(theGraph, v));
gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), eBeforePred);
}
}
else
{
gp_SetFirstArc(theGraph, u, gp_GetFirstArc(theGraph, v));
}
if (gp_IsArc(eBefore))
{
if (gp_IsArc(gp_GetLastArc(theGraph, v)))
{
gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), eBefore);
gp_SetPrevArc(theGraph, eBefore, gp_GetLastArc(theGraph, v));
}
}
else
{
gp_SetLastArc(theGraph, u, gp_GetLastArc(theGraph, v));
}
gp_SetFirstArc(theGraph, v, NIL);
gp_SetLastArc(theGraph, v, NIL);
}
return OK;
}
/********************************************************************
gp_RestoreVertex()
This method assumes the built-in graph stack contents are the result
of vertex hide, vertex identify and edge contract operations.
This content consists of segments of integers, each segment
corresponding to the removal of a vertex during an edge contraction
or vertex identification in which a vertex v was merged into a
vertex u. The segment contains two blocks of integers.
The first block contains information about u, v, the edge records
in v's adjacency list that were added to u, and where in u's
adjacency list they were added. The second block of integers
contains a list of edges incident to v that were hidden from the
graph because they were incident to neighbors of v that were also
neighbors of u (so they would have produced duplicate edges had
they been left in v's adjacency list when it was merged with u's
adjacency list).
This method pops the first block of the segment off the stack and
uses the information to help remove v's adjacency list from u and
restore it into v. Then, the second block is removed from the
stack, and each indicated edge is restored from the hidden state.
It is anticipated that this method will be overloaded by extension
algorithms to perform some processing as each vertex is restored.
Before restoration, the topmost segment has the following structure:
... FHE ... LHE HESB e_u_succ e_v_last e_v_first e_u_pred u v
^------------|
FHE = First hidden edge
LHE = Last hidden edge
HESB = Hidden edge stack bottom
e_u_succ, e_u_pred = The edges of u between which the edges of v
were inserted. NIL can appear if the edges of v
were added to the beginning or end of u's list
e_v_first, e_v_last = The first and last edges of v's list, once
the hidden edges were removed
Returns OK for success, NOTOK for internal failure.
********************************************************************/
int gp_RestoreVertex(graphP theGraph)
{
return theGraph->functions.fpRestoreVertex(theGraph);
}
int _RestoreVertex(graphP theGraph)
{
int u, v, e_u_succ, e_u_pred, e_v_first, e_v_last, HESB, e;
if (sp_GetCurrentSize(theGraph->theStack) < 7)
return NOTOK;
sp_Pop(theGraph->theStack, v);
sp_Pop(theGraph->theStack, u);
sp_Pop(theGraph->theStack, e_u_pred);
sp_Pop(theGraph->theStack, e_v_first);
sp_Pop(theGraph->theStack, e_v_last);
sp_Pop(theGraph->theStack, e_u_succ);
// If u is not NIL, then vertex v was identified with u. Otherwise, v was
// simply hidden, so we skip to restoring the hidden edges.
if (gp_IsVertex(u))
{
// Remove v's adjacency list from u, including accounting for degree 0 case
if (gp_IsArc(e_u_pred))
{
gp_SetNextArc(theGraph, e_u_pred, e_u_succ);
// If the successor edge exists, link it to the predecessor,
// otherwise the predecessor is the new last arc
if (gp_IsArc(e_u_succ))
gp_SetPrevArc(theGraph, e_u_succ, e_u_pred);
else
gp_SetLastArc(theGraph, u, e_u_pred);
}
else if (gp_IsArc(e_u_succ))
{
// The successor arc exists, but not the predecessor,
// so the successor is the new first arc
gp_SetPrevArc(theGraph, e_u_succ, NIL);
gp_SetFirstArc(theGraph, u, e_u_succ);
}
else
{
// Just in case u was degree zero
gp_SetFirstArc(theGraph, u, NIL);
gp_SetLastArc(theGraph, u, NIL);
}
// Place v's adjacency list into v, including accounting for degree 0 case
gp_SetFirstArc(theGraph, v, e_v_first);
gp_SetLastArc(theGraph, v, e_v_last);
if (gp_IsArc(e_v_first))
gp_SetPrevArc(theGraph, e_v_first, NIL);
if (gp_IsArc(e_v_last))
gp_SetPrevArc(theGraph, e_v_last, NIL);
// For each edge record restored to v's adjacency list, reassign the 'v' member
// of each twin arc to indicate v rather than u.
e = e_v_first;
while (gp_IsArc(e))
{
gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), v);
e = (e == e_v_last ? NIL : gp_GetNextArc(theGraph, e));
}
}
// Restore the hidden edges of v, if any
sp_Pop(theGraph->theStack, HESB);
return _RestoreHiddenEdges(theGraph, HESB);
}
/********************************************************************
gp_RestoreVertices()
This method assumes the built-in graph stack has content consistent
with numerous vertex identification or edge contraction operations.
This method unwinds the stack, moving edges back to their original
vertex owners and restoring hidden edges.
This method is a simple iterator that invokes gp_RestoreVertex()
until the stack is empty, so extension algorithms are more likely
to overload gp_RestoreVertex().
Returns OK for success, NOTOK for internal failure.
********************************************************************/
int gp_RestoreVertices(graphP theGraph)
{
while (sp_NonEmpty(theGraph->theStack))
{
if (gp_RestoreVertex(theGraph) != OK)
return NOTOK;
}
return OK;
}
/****************************************************************************
_ComputeArcType()
This is just a little helper function that automates a sequence of decisions
that has to be made a number of times.
An edge record is being added to the adjacency list of a; it indicates that
b is a neighbor. The edgeType can be either 'tree' (EDGE_TYPE_PARENT or
EDGE_TYPE_CHILD) or 'cycle' (EDGE_TYPE_BACK or EDGE_TYPE_FORWARD).
If a or b is a root copy, we translate to the non-virtual counterpart,
then wedetermine which has the lesser DFI. If a has the lower DFI then the
edge record is a tree edge to a child (EDGE_TYPE_CHILD) if edgeType indicates
a tree edge. If edgeType indicates a cycle edge, then it is a forward cycle
edge (EDGE_TYPE_FORWARD) to a descendant.
Symmetric conditions define the types for a > b.
****************************************************************************/
int _ComputeArcType(graphP theGraph, int a, int b, int edgeType)
{
a = gp_IsVirtualVertex(theGraph, a) ? gp_GetPrimaryVertexFromRoot(theGraph, a) : a;
b = gp_IsVirtualVertex(theGraph, b) ? gp_GetPrimaryVertexFromRoot(theGraph, b) : b;
if (a < b)
return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_CHILD : EDGE_TYPE_FORWARD;
return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_PARENT : EDGE_TYPE_BACK;
}
/****************************************************************************
_SetEdgeType()
When we are restoring an edge, we must restore its type (tree edge or cycle edge).
We can deduce what the type was based on other information in the graph. Each
arc of the edge gets the appropriate type setting (parent/child or back/forward).
This method runs in constant time plus the degree of vertex u, or constant
time if u is known to have a degree bound by a constant.
****************************************************************************/
int _SetEdgeType(graphP theGraph, int u, int v)
{
int e, eTwin, u_orig, v_orig;
// If u or v is a virtual vertex (a root copy), then get the non-virtual counterpart.
u_orig = gp_IsVirtualVertex(theGraph, u) ? (gp_GetPrimaryVertexFromRoot(theGraph, u)) : u;
v_orig = gp_IsVirtualVertex(theGraph, v) ? (gp_GetPrimaryVertexFromRoot(theGraph, v)) : v;
// Get the edge for which we will set the type
e = gp_GetNeighborEdgeRecord(theGraph, u, v);
eTwin = gp_GetTwinArc(theGraph, e);
// If u_orig is the parent of v_orig, or vice versa, then the edge is a tree edge
if (gp_GetVertexParent(theGraph, v_orig) == u_orig ||
gp_GetVertexParent(theGraph, u_orig) == v_orig)
{
if (u_orig > v_orig)
{
gp_ResetEdgeType(theGraph, e, EDGE_TYPE_PARENT);
gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_CHILD);
}
else
{
gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_PARENT);
gp_ResetEdgeType(theGraph, e, EDGE_TYPE_CHILD);
}
}
// Otherwise it is a back edge
else
{
if (u_orig > v_orig)
{
gp_ResetEdgeType(theGraph, e, EDGE_TYPE_BACK);
gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD);
}
else
{
gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_BACK);
gp_ResetEdgeType(theGraph, e, EDGE_TYPE_FORWARD);
}
}
return OK;
}
/********************************************************************
_DeleteUnmarkedEdgesInBicomp()
This function deletes from a given biconnected component all edges
whose visited member is zero.
The stack is used but preserved. In debug mode, NOTOK can result if
there is a stack overflow. This method pushes at most one integer
per vertex in the bicomp.
Returns OK on success, NOTOK on implementation failure
********************************************************************/
int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot)
{
int V, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
e = gp_GetFirstArc(theGraph, V);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetEdgeVisited(theGraph, e) ? gp_GetNextArc(theGraph, e) : gp_DeleteEdge(theGraph, e, 0);
}
}
return OK;
}
/********************************************************************
_ClearInvertedFlagsInBicomp()
This function clears the inverted flag markers on any edges in a
given biconnected component.
The stack is used but preserved. In debug mode, NOTOK can result if
there is a stack overflow. This method pushes at most one integer
per vertex in the bicomp.
Returns OK on success, NOTOK on implementation failure
********************************************************************/
int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot)
{
int V, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
e = gp_GetFirstArc(theGraph, V);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
{
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
gp_ClearEdgeFlagInverted(theGraph, e);
}
e = gp_GetNextArc(theGraph, e);
}
}
return OK;
}
/********************************************************************
_GetBicompSize()
Determine the number of vertices in the bicomp.
The stack is used but preserved. In debug mode, NOTOK can result if
there is a stack overflow. This method pushes at most one integer
per vertex in the bicomp.
Returns a positive number on success, NOTOK on implementation failure
********************************************************************/
int _GetBicompSize(graphP theGraph, int BicompRoot)
{
int V, e;
int theSize = 0;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
sp_Pop(theGraph->theStack, V);
theSize++;
e = gp_GetFirstArc(theGraph, V);
while (gp_IsArc(e))
{
if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));
e = gp_GetNextArc(theGraph, e);
}
}
return theSize;
}
/********************************************************************
debugNOTOK()
This function provides a non-void wrapper for exit().
This is useful for debugging as it allows compilation of an exit
command in places where NOTOK is returned.
In exhaustive testing, we want to bail on the first NOTOK that occurs.
Comment out the exit() call to get a stack trace.
********************************************************************/
int debugNOTOK()
{
//exit(-1);
return 0; // NOTOK is normally defined to be zero
}
edge-addition-planarity-suite-Version_3.0.0.5/c/listcoll.c 0000664 0000000 0000000 00000022462 12716155161 0023435 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#define _LISTCOLL_C
#include "appconst.h"
#include "listcoll.h"
#include
/*****************************************************************************
The data structure defined by this module manages a set of N objects
arranged as a collection of circular lists, each containing distinct
elements from the set.
On construction, LCNew() creates an array of N nodes, each containing a
prev and next pointer. The identity of the node is given by its array index.
Each node's prev and next pointers are set to NIL, indicating that the node
is not currently part of a list. LCReset() can be called to reset all
pointers to NIL.
The function LCFree() deallocates the collection of lists and clears the
pointer variable used to pass the collection.
An empty list is indicated by NIL. To begin a list with node I, call
LCPrepend() or LCAppend() with the NIL list and with I as the node. The prev
and next pointers in node I are set to I and I is returned as the head of
the list.
Future calls to LCPrepend() add a node J as the new first element of the list,
so the list given as input is pointed to by J's next, and J is returned as
the head of the list.
Future calls to LCAppend() add a node J as the new last element, so the prev
pointer of the list given as input will indicate node J, and the input list
is returned as the head of the list.
LCInsertAfter() adds a node immediately after a given anchor node.
LCInsertBefore() adds a node immediately before a given anchor node and has
the same effect on a list as LCPrepend().
The function LCDelete() removes a node I from a list L. If node I is in the
list alone, then its pointers are set to NIL, and NIL is returned as the list.
If node I is not alone in the list, but it is the head of the list (in other
words, I is equal to L), then L's sucessor is returned as the new head of the
list. Whether or not I equals L, node I is deleted by joining its predecessor
and successor nodes.
LCCopy() copies the contents of one collection to another if both are of
equal size.
LCGetNext() is used for forward iteration through a list in the collection.
The expected iteration pattern is first to process the node one has, then call
LCGetNext() to get the next node, so if the result of LCGetNext() would be the
head of the list, then NIL is returned instead. This simplifies most
coding operations involving LCGetNext().
LCGetPrev() is used for backward iteration through a list in the collection.
The expected iteration pattern is that the last list element will be obtained
by an initial call to LCGetPrev() with theNode equal to NIL. This call
should appear outside of the iteration loop. The iteration loop then
proceeds while the current node is not NIL. The loop body processes the
current node, then LCGetPrev() is called with theNode equal to the current
node. LCGetPrev() returns NIL if theNode is equal to theList. Otherwise,
the predecessor of theNode is returned.
*****************************************************************************/
/*****************************************************************************
LCNew()
*****************************************************************************/
listCollectionP LCNew(int N)
{
listCollectionP theListColl = NULL;
if (N <= 0) return theListColl;
theListColl = (listCollectionP) malloc(sizeof(listCollectionRec));
if (theListColl != NULL)
{
theListColl->List = (lcnode *) malloc(N*sizeof(lcnode));
if (theListColl->List == NULL)
{
free(theListColl);
theListColl = NULL;
}
else
{
theListColl->N = N;
LCReset(theListColl);
}
}
return theListColl;
}
/*****************************************************************************
LCFree()
*****************************************************************************/
void LCFree(listCollectionP *pListColl)
{
if (pListColl==NULL || *pListColl==NULL) return;
if ((*pListColl)->List != NULL)
free((*pListColl)->List);
free(*pListColl);
*pListColl = NULL;
}
/*****************************************************************************
LCInsertAfter()
*****************************************************************************/
void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode)
{
listColl->List[theNewNode].prev = theAnchor;
listColl->List[theNewNode].next = listColl->List[theAnchor].next;
listColl->List[listColl->List[theAnchor].next].prev = theNewNode;
listColl->List[theAnchor].next = theNewNode;
}
/*****************************************************************************
LCInsertBefore()
*****************************************************************************/
void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode)
{
LCPrepend(listColl, theAnchor, theNewNode);
}
#ifndef SPEED_MACROS
/*****************************************************************************
LCReset()
*****************************************************************************/
void LCReset(listCollectionP listColl)
{
int K;
for (K=0; K < listColl->N; K++)
listColl->List[K].prev = listColl->List[K].next = NIL;
}
/*****************************************************************************
LCCopy()
*****************************************************************************/
void LCCopy(listCollectionP dst, listCollectionP src)
{
int K;
if (dst==NULL || src==NULL || dst->N != src->N) return;
for (K=0; K < dst->N; K++)
dst->List[K] = src->List[K];
}
/*****************************************************************************
LCGetNext()
*****************************************************************************/
int LCGetNext(listCollectionP listColl, int theList, int theNode)
{
int next;
if (listColl==NULL || theList==NIL || theNode==NIL) return NIL;
next = listColl->List[theNode].next;
return next==theList ? NIL : next;
}
/*****************************************************************************
LCGetPrev()
*****************************************************************************/
int LCGetPrev(listCollectionP listColl, int theList, int theNode)
{
if (listColl==NULL || theList==NIL) return NIL;
if (theNode == NIL) return listColl->List[theList].prev;
if (theNode == theList) return NIL;
return listColl->List[theNode].prev;
}
/*****************************************************************************
LCPrepend()
*****************************************************************************/
int LCPrepend(listCollectionP listColl, int theList, int theNode)
{
/* If the append worked, then theNode is last, which in a circular
list is the direct predecessor of the list head node, so we
just back up one. For singletons, the result is unchanged. */
return listColl->List[LCAppend(listColl, theList, theNode)].prev;
}
/*****************************************************************************
LCAppend()
*****************************************************************************/
int LCAppend(listCollectionP listColl, int theList, int theNode)
{
/* If the given list is empty, then the given node becomes the
singleton list output */
if (theList == NIL)
{
listColl->List[theNode].prev = listColl->List[theNode].next = theNode;
theList = theNode;
}
/* Otherwise, make theNode the predecessor of head node of theList,
which is where the last node goes in a circular list. */
else
{
int pred = listColl->List[theList].prev;
listColl->List[theList].prev = theNode;
listColl->List[theNode].next = theList;
listColl->List[theNode].prev = pred;
listColl->List[pred].next = theNode;
}
/* Return the list (only really important if it was NIL) */
return theList;
}
/*****************************************************************************
LCDelete()
*****************************************************************************/
int LCDelete(listCollectionP listColl, int theList, int theNode)
{
/* If the list is a singleton, then NIL its pointers and
return NIL for theList*/
if (listColl->List[theList].next == theList)
{
listColl->List[theList].prev = listColl->List[theList].next = NIL;
theList = NIL;
}
/* Join predecessor and successor, dropping theNode from the list.
If theNode is the head of the list, then return the successor as
the new head node. */
else
{
int pred=listColl->List[theNode].prev,
succ=listColl->List[theNode].next;
listColl->List[pred].next = succ;
listColl->List[succ].prev = pred;
listColl->List[theNode].prev = listColl->List[theNode].next = NIL;
if (theList == theNode)
theList = succ;
}
return theList;
}
#endif // SPEED_MACROS
edge-addition-planarity-suite-Version_3.0.0.5/c/listcoll.h 0000664 0000000 0000000 00000010211 12716155161 0023427 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifndef _LISTCOLL_H
#define _LISTCOLL_H
#ifdef __cplusplus
extern "C" {
#endif
/* This include is needed for memset and memcpy */
#include
typedef struct
{
int prev, next;
} lcnode;
typedef struct
{
int N;
lcnode *List;
} listCollectionRec;
typedef listCollectionRec * listCollectionP;
listCollectionP LCNew(int N);
void LCFree(listCollectionP *pListColl);
void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode);
void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode);
#ifndef SPEED_MACROS
void LCReset(listCollectionP listColl);
void LCCopy(listCollectionP dst, listCollectionP src);
int LCGetNext(listCollectionP listColl, int theList, int theNode);
int LCGetPrev(listCollectionP listColl, int theList, int theNode);
int LCPrepend(listCollectionP listColl, int theList, int theNode);
int LCAppend(listCollectionP listColl, int theList, int theNode);
int LCDelete(listCollectionP listColl, int theList, int theNode);
#else
/* void LCReset(listCollectionP listColl); */
#define LCReset(listColl) memset(listColl->List, NIL_CHAR, listColl->N*sizeof(lcnode))
/* void LCCopy(listCollectionP dst, listCollectionP src) */
#define LCCopy(dst, src) memcpy(dst->List, src->List, src->N*sizeof(lcnode))
/* int LCGetNext(listCollectionP listColl, int theList, int theNode);
Return theNode's successor, unless it is theList head pointer */
#define LCGetNext(listColl, theList, theNode) listColl->List[theNode].next==theList ? NIL : listColl->List[theNode].next
/* int LCGetPrev(listCollectionP listColl, int theList, int theNode);
Return theNode's predecessor unless theNode is theList head.
To start going backwards, use NIL for theNode, which returns theList head's predecessor
Usage: Obtain last node, loop while NIL not returned, process node then get predecessor.
After theList head processed, get predecessor returns NIL because we started with
theList head's predecessor. */
#define LCGetPrev(listColl, theList, theNode) \
(theNode==NIL \
? listColl->List[theList].prev \
: theNode==theList ? NIL : listColl->List[theNode].prev)
/* int LCPrepend(listCollectionP listColl, int theList, int theNode);
After an append, theNode is last, which in a circular list is the direct predecessor
of the list head node, so we just back up one. For singletons, this has no effect.*/
#define LCPrepend(listColl, theList, theNode) listColl->List[LCAppend(listColl, theList, theNode)].prev
/* int LCAppend(listCollectionP listColl, int theList, int theNode);
If theList is empty, then theNode becomes its only member and is returned.
Otherwise, theNode is placed before theList head, which is returned. */
#define LCAppend(listColl, theList, theNode) \
(theList==NIL \
? (listColl->List[theNode].prev = listColl->List[theNode].next = theNode) \
: (listColl->List[theNode].next = theList, \
listColl->List[theNode].prev = listColl->List[theList].prev, \
listColl->List[listColl->List[theNode].prev].next = theNode, \
listColl->List[theList].prev = theNode, \
theList))
/* int LCDelete(listCollectionP listColl, int theList, int theNode);
If theList contains only one node, then NIL it out and return NIL meaning empty list
Otherwise, join the predecessor and successor, then
return either the list head or its successor if the deleted node is the list head
(in that case, the caller makes the successor become the new list head).*/
#define LCDelete(listColl, theList, theNode) \
listColl->List[theList].next == theList \
? (listColl->List[theList].prev = listColl->List[theList].next = NIL) \
: (listColl->List[listColl->List[theNode].prev].next = listColl->List[theNode].next, \
listColl->List[listColl->List[theNode].next].prev = listColl->List[theNode].prev, \
(theList==theNode ? listColl->List[theNode].next : theList))
#endif
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/planarity.c 0000664 0000000 0000000 00000021362 12716155161 0023611 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "planarity.h"
void ProjectTitle()
{
// This message is the main location of the version number.
// The format is major.minor.maintenance.tweak
// Major is for an overhaul (e.g. many features, data structure change)
// Minor is for feature addition, e.g. a new algorithm implementation added
// Maintenance is for functional revision, e.g. bug fix to algorithm implementation
// Tweak is for a non-functional revision, e.g. change of build scripts or testing code
// If the version here is increased, also increase it in configure.ac
// Furthermore, a change of Major, Minor or Maintenance here should cause a change
// of Current, Revision and/or Age as documented in configure.ac
Message("\n=================================================="
"\nThe Edge Addition Planarity Suite version 3.0.0.5"
"\nCopyright (c) 1997-2016 by John M. Boyer"
"\nContact info: jboyer at acm.org"
"\n=================================================="
"\n");
}
/****************************************************************************
MAIN
****************************************************************************/
int main(int argc, char *argv[])
{
int retVal=0;
if (argc <= 1)
retVal = menu();
else if (argv[1][0] == '-')
retVal = commandLine(argc, argv);
else
retVal = legacyCommandLine(argc, argv);
// Close the log file if logging
gp_Log(NULL);
return retVal;
}
/****************************************************************************
helpMessage()
****************************************************************************/
int helpMessage(char *param)
{
char *commandStr =
"C = command (algorithm implementation to run)\n"
" -p = Planar embedding and Kuratowski subgraph isolation\n"
" -d = Planar graph drawing by visibility representation\n"
" -o = Outerplanar embedding and obstruction isolation\n"
" -2 = Search for subgraph homeomorphic to K_{2,3}\n"
" -3 = Search for subgraph homeomorphic to K_{3,3}\n"
" -4 = Search for subgraph homeomorphic to K_4\n"
" -a = All of the above\n"
"\n";
ProjectTitle();
if (param == NULL)
{
Message(
"'planarity': if no command-line, then menu-driven\n"
"'planarity (-h|-help)': this message\n"
"'planarity (-h|-help) -gen': more help with graph generator command line\n"
"'planarity (-h|-help) -menu': more help with menu-based command line\n"
"'planarity (-i|-info): copyright and license information\n"
"'planarity -test [-q] [C]': runs tests (optional quiet mode, single test)\n"
"\n"
);
Message(
"Common usages\n"
"-------------\n"
"planarity -s -q -p infile.txt embedding.out [obstruction.out]\n"
"Process infile.txt in quiet mode (-q), putting planar embedding in \n"
"embedding.out or (optionally) a Kuratowski subgraph in Obstruction.out\n"
"Process returns 0=planar, 1=nonplanar, -1=error\n"
"\n"
"planarity -s -q -d infile.txt embedding.out [drawing.out]\n"
"If graph in infile.txt is planar, then put embedding in embedding.out \n"
"and (optionally) an ASCII art drawing in drawing.out\n"
"Process returns 0=planar, 1=nonplanar, -1=error\n"
);
}
else if (strcmp(param, "-i") == 0 || strcmp(param, "-info") == 0)
{
Message(
"The Edge Addition Planarity Suite\n"
"Copyright (c) 1997-2015, John M. Boyer\n"
"All rights reserved. \n"
"See the LICENSE.TXT file for licensing information. \n"
"\n"
"Includes a reference implementation of the following:\n"
"\n"
"* John M. Boyer. \"Subgraph Homeomorphism via the Edge Addition Planarity \n"
" Algorithm\". Journal of Graph Algorithms and Applications, Vol. 16, \n"
" no. 2, pp. 381-410, 2012. http://www.jgaa.info/16/268.html\n"
"\n"
"* John M. Boyer. \"A New Method for Efficiently Generating Planar Graph\n"
" Visibility Representations\". In P. Eades and P. Healy, editors,\n"
" Proceedings of the 13th International Conference on Graph Drawing 2005,\n"
" Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.\n"
"\n"
"* John M. Boyer and Wendy J. Myrvold. \"On the Cutting Edge: Simplified O(n)\n"
" Planarity by Edge Addition\". Journal of Graph Algorithms and Applications,\n"
" Vol. 8, No. 3, pp. 241-273, 2004.\n"
" http://www.jgaa.info/08/91.html\n"
"\n"
"* John M. Boyer. \"Simplified O(n) Algorithms for Planar Graph Embedding,\n"
" Kuratowski Subgraph Isolation, and Related Problems\". Ph.D. Dissertation,\n"
" University of Victoria, 2001.\n"
"\n"
);
}
else if (strcmp(param, "-menu") == 0)
{
Message(
"'planarity -r [-q] C K N': Random graphs\n"
"'planarity -s [-q] C I O [O2]': Specific graph\n"
"'planarity -rm [-q] N O [O2]': Maximal planar random graph\n"
"'planarity -rn [-q] N O [O2]': Nonplanar random graph (maximal planar + edge)\n"
"'planarity I O [-n O2]': Legacy command-line (default -s -p)\n"
"\n"
);
Message("-q is for quiet mode (no messages to stdout and stderr)\n\n");
Message(commandStr);
Message(
"K = # of graphs to randomly generate\n"
"N = # of vertices in each randomly generated graph\n"
"I = Input file (for work on a specific graph)\n"
"O = Primary output file\n"
" For example, if C=-p then O receives the planar embedding\n"
" If C=-3, then O receives a subgraph containing a K_{3,3}\n"
"O2= Secondary output file\n"
" For -s, if C=-p or -o, then O2 receives the embedding obstruction\n"
" For -s, if C=-d, then O2 receives a drawing of the planar graph\n"
" For -m and -n, O2 contains the original randomly generated graph\n"
"\n"
);
Message(
"planarity process results: 0=OK, -1=NOTOK, 1=NONEMBEDDABLE\n"
" 1 result only produced by specific graph mode (-s)\n"
" with command -2,-3,-4: found K_{2,3}, K_{3,3} or K_4\n"
" with command -p,-d: found planarity obstruction\n"
" with command -o: found outerplanarity obstruction\n"
);
}
FlushConsole(stdout);
return 0;
}
/****************************************************************************
MENU-DRIVEN PROGRAM
****************************************************************************/
int menu()
{
char Choice;
do {
ProjectTitle();
Message("\n"
"P. Planar embedding and Kuratowski subgraph isolation\n"
"D. Planar graph drawing by visibility representation\n"
"O. Outerplanar embedding and obstruction isolation\n"
"2. Search for subgraph homeomorphic to K_{2,3}\n"
"3. Search for subgraph homeomorphic to K_{3,3}\n"
"4. Search for subgraph homeomorphic to K_4\n"
"H. Help message for command line version\n"
"R. Reconfigure options\n"
"X. Exit\n"
"\n"
);
Prompt("Enter Choice: ");
fflush(stdin);
scanf(" %c", &Choice);
Choice = tolower(Choice);
if (Choice == 'h')
helpMessage(NULL);
else if (Choice == 'r')
Reconfigure();
else if (Choice != 'x')
{
char *secondOutfile = NULL;
if (Choice == 'p' || Choice == 'o' || Choice == 'd')
secondOutfile ="";
switch (tolower(Mode))
{
case 's' : SpecificGraph(Choice, NULL, NULL, secondOutfile); break;
case 'r' : RandomGraphs(Choice, 0, 0); break;
case 'm' : RandomGraph(Choice, 0, 0, NULL, NULL); break;
case 'n' : RandomGraph(Choice, 1, 0, NULL, NULL); break;
}
}
if (Choice != 'r' && Choice != 'x')
{
Prompt("\nPress a key then hit ENTER to continue...");
fflush(stdin);
scanf(" %*c");
fflush(stdin);
Message("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
FlushConsole(stdout);
}
} while (Choice != 'x');
// Certain debuggers don't terminate correctly with pending output content
FlushConsole(stdout);
FlushConsole(stderr);
return 0;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/planarity.h 0000664 0000000 0000000 00000003560 12716155161 0023616 0 ustar 00root root 0000000 0000000 #ifndef PLANARITY_H
#define PLANARITY_H
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include
#include
#include "graph.h"
#include "platformTime.h"
#include "graphK23Search.h"
#include "graphK33Search.h"
#include "graphK4Search.h"
#include "graphDrawPlanar.h"
void ProjectTitle();
int helpMessage(char *param);
/* Functions that call the Graph Library */
int SpecificGraph(char command, char *infileName, char *outfileName, char *outfile2Name);
int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name);
int RandomGraphs(char command, int, int);
int makeg_main(char command, int argc, char *argv[]);
/* Command line, Menu, and Configuration */
int commandLine(int argc, char *argv[]);
int legacyCommandLine(int argc, char *argv[]);
int menu();
char Mode,
OrigOut,
EmbeddableOut,
ObstructedOut,
AdjListsForEmbeddingsOut,
quietMode;
void Reconfigure();
/* Low-level Utilities */
#define MAXLINE 1024
char Line[MAXLINE];
void Message(char *message);
void ErrorMessage(char *message);
void FlushConsole(FILE *f);
void Prompt(char *message);
void SaveAsciiGraph(graphP theGraph, char *filename);
int TextFilesEqual(char *file1Name, char *file2Name);
int GetEmbedFlags(char command);
char *GetAlgorithmName(char command);
void AttachAlgorithm(graphP theGraph, char command);
char *ConstructInputFilename(char *infileName);
char *ConstructPrimaryOutputFilename(char *infileName, char *outfileName, char command);
void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName);
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/planarityCommandLine.c 0000664 0000000 0000000 00000026427 12716155161 0025727 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "planarity.h"
#include
int runQuickRegressionTests(int argc, char *argv[]);
int callRandomGraphs(int argc, char *argv[]);
int callSpecificGraph(int argc, char *argv[]);
int callRandomMaxPlanarGraph(int argc, char *argv[]);
int callRandomNonplanarGraph(int argc, char *argv[]);
/****************************************************************************
Command Line Processor
****************************************************************************/
int commandLine(int argc, char *argv[])
{
int Result = OK;
if (argc >= 3 && strcmp(argv[2], "-q") == 0)
quietMode = 'y';
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0)
{
Result = helpMessage(argc >= 3 ? argv[2] : NULL);
}
else if (strcmp(argv[1], "-i") == 0 || strcmp(argv[1], "-info") == 0)
{
Result = helpMessage(argv[1]);
}
else if (strcmp(argv[1], "-test") == 0)
Result = runQuickRegressionTests(argc, argv);
else if (strcmp(argv[1], "-r") == 0)
Result = callRandomGraphs(argc, argv);
else if (strcmp(argv[1], "-s") == 0)
Result = callSpecificGraph(argc, argv);
else if (strcmp(argv[1], "-rm") == 0)
Result = callRandomMaxPlanarGraph(argc, argv);
else if (strcmp(argv[1], "-rn") == 0)
Result = callRandomNonplanarGraph(argc, argv);
else
{
ErrorMessage("Unsupported command line. Here is the help for this program.\n");
helpMessage(NULL);
Result = NOTOK;
}
return Result == OK ? 0 : (Result == NONEMBEDDABLE ? 1 : -1);
}
/****************************************************************************
Legacy Command Line Processor from version 1.x
****************************************************************************/
int legacyCommandLine(int argc, char *argv[])
{
graphP theGraph = gp_New();
int Result;
Result = gp_Read(theGraph, argv[1]);
if (Result != OK)
{
if (Result != NONEMBEDDABLE)
{
if (strlen(argv[1]) > MAXLINE - 100)
sprintf(Line, "Failed to read graph\n");
else
sprintf(Line, "Failed to read graph %s\n", argv[1]);
ErrorMessage(Line);
return -2;
}
}
Result = gp_Embed(theGraph, EMBEDFLAGS_PLANAR);
if (Result == OK)
{
gp_SortVertices(theGraph);
gp_Write(theGraph, argv[2], WRITE_ADJLIST);
}
else if (Result == NONEMBEDDABLE)
{
if (argc >= 5 && strcmp(argv[3], "-n")==0)
{
gp_SortVertices(theGraph);
gp_Write(theGraph, argv[4], WRITE_ADJLIST);
}
}
else
Result = NOTOK;
gp_Free(&theGraph);
// In the legacy 1.x versions, OK/NONEMBEDDABLE was 0 and NOTOK was -2
return Result==OK || Result==NONEMBEDDABLE ? 0 : -2;
}
/****************************************************************************
WriteTestFiles()
A one-time-run piece of code that generates batch files which perform the
incremental tests of all 12 vertex graphs.
The format of the command line is:
planarity -gen -q -a n j j 12 i > TestResult_n_j_j_12_i.txt
The n is for the number of vertices.
The j j is the number of edges, between 0 and n(n-1)/2.
The mod divides the problem into partitions that can be run on separate threads.
The i is a partition number between 0 and mod-1. Each test batch file receives
a number i, and within it are all the command lines for that partition to test
each number of edges.
The numbers of edges are tested separately because the implementation makes
a smaller data structure for graphs that are of a guaranteed maximum size.
****************************************************************************/
void WriteTestFiles(int n, int mod)
{
char filename[64];
FILE *outfile;
int k, e;
int maxe=n*(n-1)/2;
for (k = 0; k < mod; k++)
{
sprintf(filename, "test_n%02d\\test_n%02d_mod%02d.bat", n, n, k);
outfile = fopen(filename, "wt");
if (outfile == NULL)
{
printf("Error creating test file %s\nRemember to create directory Test_n%02d\n", filename, n);
return;
}
for (e = 0; e <= maxe; e++)
{
sprintf(filename, "results\\result_%02d_%02d_%02d_%02d_%02d.txt", n, e, e, mod, k);
fprintf(outfile, "..\\planarity -gen -a %d %d %d %d %d > %s\n", n, e, e, mod, k, filename);
}
fclose(outfile);
}
printf("Created test files. Remember to create 'results' in test subdirectory\n");
}
/****************************************************************************
Quick regression test
****************************************************************************/
int runSpecificGraphTests();
int runSpecificGraphTest(char *command, char *infileName);
int runQuickRegressionTests(int argc, char *argv[])
{
if (runSpecificGraphTests() < 0)
return -1;
return 0;
}
int runSpecificGraphTests()
{
char origDir[2049];
int retVal = 0;
if (!getcwd(origDir, 2048))
return -1;
if (chdir("samples") != 0)
{
if (chdir("..") != 0 || chdir("samples") != 0)
{
// Warn but give success result
printf("WARNING: Unable to change to samples directory to run tests on samples.\n");
return 0;
}
}
#if NIL == 0
if (runSpecificGraphTest("-p", "maxPlanar5.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-d", "maxPlanar5.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-d", "drawExample.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-p", "Petersen.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-o", "Petersen.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-2", "Petersen.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-3", "Petersen.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-4", "Petersen.txt") < 0)
retVal = -1;
#endif
if (runSpecificGraphTest("-p", "maxPlanar5.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-d", "maxPlanar5.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-d", "drawExample.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-p", "Petersen.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-o", "Petersen.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-2", "Petersen.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-3", "Petersen.0-based.txt") < 0)
retVal = -1;
if (runSpecificGraphTest("-4", "Petersen.0-based.txt") < 0)
retVal = -1;
if (retVal == 0)
printf("Tests of all specific graphs succeeded\n");
chdir(origDir);
FlushConsole(stdout);
return retVal;
}
int runSpecificGraphTest(char *command, char *infileName)
{
char *commandLine[] = {
"planarity", "-s", "C", "infile", "outfile", "outfile2"
};
char *outfileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]);
char *outfile2Name = "";
char *testfileName = strdup(outfileName);
int Result = 0;
if (testfileName == NULL)
return -1;
outfileName = strdup(strcat(outfileName, ".test.txt"));
if (outfileName == NULL)
{
free(testfileName);
return -1;
}
// 'planarity -s [-q] C I O [O2]': Specific graph
commandLine[2] = command;
commandLine[3] = infileName;
commandLine[4] = outfileName;
commandLine[5] = outfile2Name;
Result = callSpecificGraph(6, commandLine);
if (Result == OK || Result == NONEMBEDDABLE)
Result = 0;
else
{
ErrorMessage("Test failed (graph processor returned failure result).\n");
Result = -1;
}
if (Result == 0)
{
if (TextFilesEqual(testfileName, outfileName) == TRUE)
{
Message("Test succeeded (result equal to exemplar).\n");
unlink(outfileName);
}
else
{
ErrorMessage("Test failed (result not equal to exemplar).\n");
Result = -1;
}
}
// For graph drawing, secondary file is outfileName + ".render.txt"
if (command[1] == 'd' && Result == 0)
{
outfile2Name = ConstructPrimaryOutputFilename(NULL, outfileName, command[1]);
free(outfileName);
outfileName = strdup(strcat(outfile2Name, ".render.txt"));
free(testfileName);
testfileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]);
testfileName = strdup(strcat(testfileName, ".render.txt"));
if (Result == 0)
{
if (TextFilesEqual(testfileName, outfileName) == TRUE)
{
Message("Test succeeded (secondary result equal to exemplar).\n");
unlink(outfileName);
}
else
{
ErrorMessage("Test failed (secondary result not equal to exemplar).\n");
Result = -1;
}
}
}
Message("\n");
free(outfileName);
free(testfileName);
return Result;
}
/****************************************************************************
callRandomGraphs()
****************************************************************************/
// 'planarity -r [-q] C K N': Random graphs
int callRandomGraphs(int argc, char *argv[])
{
char Choice = 0;
int offset = 0, NumGraphs, SizeOfGraphs;
if (argc < 5)
return -1;
if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q')
{
Choice = argv[3][1];
if (argc < 6)
return -1;
offset = 1;
}
NumGraphs = atoi(argv[3+offset]);
SizeOfGraphs = atoi(argv[4+offset]);
return RandomGraphs(Choice, NumGraphs, SizeOfGraphs);
}
/****************************************************************************
callSpecificGraph()
****************************************************************************/
// 'planarity -s [-q] C I O [O2]': Specific graph
int callSpecificGraph(int argc, char *argv[])
{
char Choice=0, *infileName=NULL, *outfileName=NULL, *outfile2Name=NULL;
int offset = 0;
if (argc < 5)
return -1;
if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q')
{
Choice = argv[3][1];
if (argc < 6)
return -1;
offset = 1;
}
infileName = argv[3+offset];
outfileName = argv[4+offset];
if (argc == 6+offset)
outfile2Name = argv[5+offset];
return SpecificGraph(Choice, infileName, outfileName, outfile2Name);
}
/****************************************************************************
callRandomMaxPlanarGraph()
****************************************************************************/
// 'planarity -rm [-q] N O [O2]': Maximal planar random graph
int callRandomMaxPlanarGraph(int argc, char *argv[])
{
int offset = 0, numVertices;
char *outfileName = NULL, *outfile2Name = NULL;
if (argc < 4)
return -1;
if (argv[2][0] == '-' && argv[2][1] == 'q')
{
if (argc < 5)
return -1;
offset = 1;
}
numVertices = atoi(argv[2+offset]);
outfileName = argv[3+offset];
if (argc == 5+offset)
outfile2Name = argv[4+offset];
return RandomGraph('p', 0, numVertices, outfileName, outfile2Name);
}
/****************************************************************************
callRandomNonplanarGraph()
****************************************************************************/
// 'planarity -rn [-q] N O [O2]': Non-planar random graph (maximal planar plus edge)
int callRandomNonplanarGraph(int argc, char *argv[])
{
int offset = 0, numVertices;
char *outfileName = NULL, *outfile2Name = NULL;
if (argc < 4)
return -1;
if (argv[2][0] == '-' && argv[2][1] == 'q')
{
if (argc < 5)
return -1;
offset = 1;
}
numVertices = atoi(argv[2+offset]);
outfileName = argv[3+offset];
if (argc == 5+offset)
outfile2Name = argv[4+offset];
return RandomGraph('p', 1, numVertices, outfileName, outfile2Name);
}
edge-addition-planarity-suite-Version_3.0.0.5/c/planarityRandomGraphs.c 0000664 0000000 0000000 00000034601 12716155161 0026117 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "planarity.h"
void GetNumberIfZero(int *pNum, char *prompt, int min, int max);
void ReinitializeGraph(graphP *pGraph, int ReuseGraphs, char command);
graphP MakeGraph(int Size, char command);
/****************************************************************************
RandomGraphs()
Top-level method to randomly generate graphs to test the algorithm given by
the command parameter.
The number of graphs to generate, and the number of vertices for each graph,
can be sent as the second and third params. For each that is sent as zero,
this method will prompt the user for a value.
****************************************************************************/
#define NUM_MINORS 9
int RandomGraphs(char command, int NumGraphs, int SizeOfGraphs)
{
char theFileName[256];
int K, countUpdateFreq;
int Result=OK, MainStatistic=0;
int ObstructionMinorFreqs[NUM_MINORS];
graphP theGraph=NULL, origGraph=NULL;
platform_time start, end;
int embedFlags = GetEmbedFlags(command);
int ReuseGraphs = TRUE;
GetNumberIfZero(&NumGraphs, "Enter number of graphs to generate:", 1, 1000000000);
GetNumberIfZero(&SizeOfGraphs, "Enter size of graphs:", 1, 10000);
theGraph = MakeGraph(SizeOfGraphs, command);
origGraph = MakeGraph(SizeOfGraphs, command);
if (theGraph == NULL || origGraph == NULL)
{
gp_Free(&theGraph);
return NOTOK;
}
// Initialize a secondary statistics array
for (K=0; K < NUM_MINORS; K++)
ObstructionMinorFreqs[K] = 0;
// Seed the random number generator with "now". Do it after any prompting
// to tie randomness to human process of answering the prompt.
srand(time(NULL));
// Select a counter update frequency that updates more frequently with larger graphs
// and which is relatively prime with 10 so that all digits of the count will change
// even though we aren't showing the count value on every iteration
countUpdateFreq = 3579 / SizeOfGraphs;
countUpdateFreq = countUpdateFreq < 1 ? 1 : countUpdateFreq;
countUpdateFreq = countUpdateFreq % 2 == 0 ? countUpdateFreq+1 : countUpdateFreq;
countUpdateFreq = countUpdateFreq % 5 == 0 ? countUpdateFreq+2 : countUpdateFreq;
// Start the count
fprintf(stdout, "0\r");
fflush(stdout);
// Start the timer
platform_GetTime(start);
// Generate and process the number of graphs requested
for (K=0; K < NumGraphs; K++)
{
if ((Result = gp_CreateRandomGraph(theGraph)) == OK)
{
if (tolower(OrigOut)=='y')
{
sprintf(theFileName, "random\\%d.txt", K%10);
gp_Write(theGraph, theFileName, WRITE_ADJLIST);
}
gp_CopyGraph(origGraph, theGraph);
if (strchr("pdo234", command))
{
Result = gp_Embed(theGraph, embedFlags);
if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != Result)
Result = NOTOK;
if (Result == OK)
{
MainStatistic++;
if (tolower(EmbeddableOut) == 'y')
{
sprintf(theFileName, "embedded\\%d.txt", K%10);
gp_Write(theGraph, theFileName, WRITE_ADJMATRIX);
}
if (tolower(AdjListsForEmbeddingsOut) == 'y')
{
sprintf(theFileName, "adjlist\\%d.txt", K%10);
gp_Write(theGraph, theFileName, WRITE_ADJLIST);
}
}
else if (Result == NONEMBEDDABLE)
{
if (embedFlags == EMBEDFLAGS_PLANAR || embedFlags == EMBEDFLAGS_OUTERPLANAR)
{
if (theGraph->IC.minorType & MINORTYPE_A)
ObstructionMinorFreqs[0] ++;
else if (theGraph->IC.minorType & MINORTYPE_B)
ObstructionMinorFreqs[1] ++;
else if (theGraph->IC.minorType & MINORTYPE_C)
ObstructionMinorFreqs[2] ++;
else if (theGraph->IC.minorType & MINORTYPE_D)
ObstructionMinorFreqs[3] ++;
else if (theGraph->IC.minorType & MINORTYPE_E)
ObstructionMinorFreqs[4] ++;
if (theGraph->IC.minorType & MINORTYPE_E1)
ObstructionMinorFreqs[5] ++;
else if (theGraph->IC.minorType & MINORTYPE_E2)
ObstructionMinorFreqs[6] ++;
else if (theGraph->IC.minorType & MINORTYPE_E3)
ObstructionMinorFreqs[7] ++;
else if (theGraph->IC.minorType & MINORTYPE_E4)
ObstructionMinorFreqs[8] ++;
if (tolower(ObstructedOut) == 'y')
{
sprintf(theFileName, "obstructed\\%d.txt", K%10);
gp_Write(theGraph, theFileName, WRITE_ADJMATRIX);
}
}
}
}
// If there is an error in processing, then write the file for debugging
if (Result != OK && Result != NONEMBEDDABLE)
{
sprintf(theFileName, "error\\%d.txt", K%10);
gp_Write(origGraph, theFileName, WRITE_ADJLIST);
}
}
// Reinitialize or recreate graphs for next iteration
ReinitializeGraph(&theGraph, ReuseGraphs, command);
ReinitializeGraph(&origGraph, ReuseGraphs, command);
// Show progress, but not so often that it bogs down progress
if (quietMode == 'n' && (K+1) % countUpdateFreq == 0)
{
fprintf(stdout, "%d\r", K+1);
fflush(stdout);
}
// Terminate loop on error
if (Result != OK && Result != NONEMBEDDABLE)
{
ErrorMessage("\nError found\n");
Result = NOTOK;
break;
}
}
// Stop the timer
platform_GetTime(end);
// Finish the count
fprintf(stdout, "%d\n", NumGraphs);
fflush(stdout);
// Free the graph structures created before the loop
gp_Free(&theGraph);
gp_Free(&origGraph);
// Print some demographic results
if (Result == OK || Result == NONEMBEDDABLE)
Message("\nNo Errors Found.");
sprintf(Line, "\nDone (%.3lf seconds).\n", platform_GetDuration(start,end));
Message(Line);
// Report statistics for planar or outerplanar embedding
if (embedFlags == EMBEDFLAGS_PLANAR || embedFlags == EMBEDFLAGS_OUTERPLANAR)
{
sprintf(Line, "Num Embedded=%d.\n", MainStatistic);
Message(Line);
for (K=0; K<5; K++)
{
// Outerplanarity does not produces minors C and D
if (embedFlags == EMBEDFLAGS_OUTERPLANAR && (K==2 || K==3))
continue;
sprintf(Line, "Minor %c = %d\n", K+'A', ObstructionMinorFreqs[K]);
Message(Line);
}
if (!(embedFlags & ~EMBEDFLAGS_PLANAR))
{
sprintf(Line, "\nNote: E1 are added to C, E2 are added to A, and E=E3+E4+K5 homeomorphs.\n");
Message(Line);
for (K=5; K max)
{
*pNum = (max + min) / 2;
sprintf(Line, "Number out of range [%d, %d]; changed to %d\n", min, max, *pNum);
ErrorMessage(Line);
}
}
/****************************************************************************
MakeGraph()
Internal function that makes a new graph, initializes it, and attaches an
algorithm to it based on the command.
****************************************************************************/
graphP MakeGraph(int Size, char command)
{
graphP theGraph;
if ((theGraph = gp_New()) == NULL || gp_InitGraph(theGraph, Size) != OK)
{
ErrorMessage("Error creating space for a graph of the given size.\n");
gp_Free(&theGraph);
return NULL;
}
// Enable the appropriate feature. Although the same code appears in SpecificGraph,
// it is deliberately not separated to a common utility because SpecificGraph is
// used as a self-contained tutorial. It is not that hard to update both locations
// when new algorithms are added.
switch (command)
{
case 'd' : gp_AttachDrawPlanar(theGraph); break;
case '2' : gp_AttachK23Search(theGraph); break;
case '3' : gp_AttachK33Search(theGraph); break;
case '4' : gp_AttachK4Search(theGraph); break;
}
return theGraph;
}
/****************************************************************************
ReinitializeGraph()
Internal function that will either reinitialize the given graph or free it
and make a new one just like it.
****************************************************************************/
void ReinitializeGraph(graphP *pGraph, int ReuseGraphs, char command)
{
if (ReuseGraphs)
gp_ReinitializeGraph(*pGraph);
else
{
graphP newGraph = MakeGraph((*pGraph)->N, command);
gp_Free(pGraph);
*pGraph = newGraph;
}
}
/****************************************************************************
Creates a random maximal planar graph, then adds 'extraEdges' edges to it.
****************************************************************************/
int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name)
{
int Result;
platform_time start, end;
graphP theGraph=NULL, origGraph;
int embedFlags = GetEmbedFlags(command);
char saveEdgeListFormat;
GetNumberIfZero(&numVertices, "Enter number of vertices:", 1, 1000000);
if ((theGraph = MakeGraph(numVertices, command)) == NULL)
return NOTOK;
srand(time(NULL));
Message("Creating the random graph...\n");
platform_GetTime(start);
if (gp_CreateRandomGraphEx(theGraph, 3*numVertices-6+extraEdges) != OK)
{
ErrorMessage("gp_CreateRandomGraphEx() failed\n");
return NOTOK;
}
platform_GetTime(end);
sprintf(Line, "Created random graph with %d edges in %.3lf seconds. ", theGraph->M, platform_GetDuration(start,end));
Message(Line);
FlushConsole(stdout);
// The user may have requested a copy of the random graph before processing
if (outfile2Name != NULL)
{
gp_Write(theGraph, outfile2Name, WRITE_ADJLIST);
}
origGraph = gp_DupGraph(theGraph);
// Do the requested algorithm on the randomly generated graph
Message("Now processing\n");
FlushConsole(stdout);
if (strchr("pdo234", command))
{
platform_GetTime(start);
Result = gp_Embed(theGraph, embedFlags);
platform_GetTime(end);
gp_SortVertices(theGraph);
if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != Result)
Result = NOTOK;
}
else
Result = NOTOK;
// Write what the algorithm determined and how long it took
WriteAlgorithmResults(theGraph, Result, command, start, end, NULL);
// On successful algorithm result, write the output file and see if the
// user wants the edge list formatted file.
if (Result == OK || Result == NONEMBEDDABLE)
{
if (outfileName != NULL)
gp_Write(theGraph, outfileName, WRITE_ADJLIST);
Prompt("Do you want to save the generated graph in edge list format (y/n)? ");
fflush(stdin);
scanf(" %c", &saveEdgeListFormat);
if (tolower(saveEdgeListFormat) == 'y')
{
char *fileName = "maxPlanarEdgeList.txt";
if (extraEdges > 0)
fileName = "nonPlanarEdgeList.txt";
SaveAsciiGraph(theGraph, fileName);
sprintf(Line, "Edge list format saved to '%s'\n", fileName);
Message(Line);
}
}
else ErrorMessage("Failure occurred");
gp_Free(&theGraph);
gp_Free(&origGraph);
FlushConsole(stdout);
return Result;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/planaritySpecificGraph.c 0000664 0000000 0000000 00000013570 12716155161 0026243 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "planarity.h"
/****************************************************************************
SpecificGraph()
****************************************************************************/
int SpecificGraph(char command, char *infileName, char *outfileName, char *outfile2Name)
{
graphP theGraph, origGraph;
platform_time start, end;
int Result;
// Get the filename of the graph to test
if ((infileName = ConstructInputFilename(infileName)) == NULL)
return NOTOK;
// Create the graph and, if needed, attach the correct algorithm to it
theGraph = gp_New();
switch (command)
{
case 'd' : gp_AttachDrawPlanar(theGraph); break;
case '2' : gp_AttachK23Search(theGraph); break;
case '3' : gp_AttachK33Search(theGraph); break;
case '4' : gp_AttachK4Search(theGraph); break;
}
// Read the graph into memory
Result = gp_Read(theGraph, infileName);
if (Result == NONEMBEDDABLE)
{
Message("The graph contains too many edges.\n");
// Some of the algorithms will still run correctly with some edges removed.
if (strchr("pdo234", command))
{
Message("Some edges were removed, but the algorithm will still run correctly.\n");
Result = OK;
}
}
// If there was an unrecoverable error, report it
if (Result != OK)
ErrorMessage("Failed to read graph\n");
// Otherwise, call the correct algorithm on it
else
{
// Copy the graph for integrity checking
origGraph = gp_DupGraph(theGraph);
// Run the algorithm
if (strchr("pdo234", command))
{
int embedFlags = GetEmbedFlags(command);
platform_GetTime(start);
// gp_CreateDFSTree(theGraph);
// gp_SortVertices(theGraph);
// gp_Write(theGraph, "debug.before.txt", WRITE_DEBUGINFO);
// gp_SortVertices(theGraph);
Result = gp_Embed(theGraph, embedFlags);
platform_GetTime(end);
Result = gp_TestEmbedResultIntegrity(theGraph, origGraph, Result);
}
else
{
platform_GetTime(start);
Result = NOTOK;
platform_GetTime(end);
}
// Write what the algorithm determined and how long it took
WriteAlgorithmResults(theGraph, Result, command, start, end, infileName);
// Free the graph obtained for integrity checking.
gp_Free(&origGraph);
}
// Report an error, if there was one, free the graph, and return
if (Result != OK && Result != NONEMBEDDABLE)
{
ErrorMessage("AN ERROR HAS BEEN DETECTED\n");
Result = NOTOK;
// gp_Write(theGraph, "debug.after.txt", WRITE_DEBUGINFO);
}
// Provide the output file(s)
else
{
// Restore the vertex ordering of the original graph (undo DFS numbering)
if (strchr("pdo234", command))
gp_SortVertices(theGraph);
// Determine the name of the primary output file
outfileName = ConstructPrimaryOutputFilename(infileName, outfileName, command);
// For some algorithms, the primary output file is not always written
if ((strchr("pdo", command) && Result == NONEMBEDDABLE) ||
(strchr("234", command) && Result == OK))
{
// Do not write the file
}
// Write the primary output file, if appropriate to do so
else
{
gp_Write(theGraph, outfileName, WRITE_ADJLIST);
}
// NOW WE WANT TO WRITE THE SECONDARY OUTPUT FILE
// When called from the menu system, we want to write the planar or outerplanar
// obstruction, if one exists. For planar graph drawing, we want the character
// art rendition. An empty but non-NULL string is passed to indicate the necessity
// of selecting a default name for the second output file.
if (outfile2Name != NULL)
{
if ((command == 'p' || command == 'o') && Result == NONEMBEDDABLE)
{
// By default, use the same name as the primary output filename
if (strlen(outfile2Name) == 0)
outfile2Name = outfileName;
gp_Write(theGraph, outfile2Name, WRITE_ADJLIST);
}
else if (command == 'd' && Result == OK)
{
// By default, add ".render.txt" to the primary output filename
if (strlen(outfile2Name) == 0)
strcat((outfile2Name = outfileName), ".render.txt");
gp_DrawPlanar_RenderToFile(theGraph, outfile2Name);
}
}
}
// Free the graph
gp_Free(&theGraph);
// Flush any remaining message content to the user, and return the result
FlushConsole(stdout);
return Result;
}
/****************************************************************************
WriteAlgorithmResults()
****************************************************************************/
void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName)
{
if (infileName)
sprintf(Line, "The graph '%s' ", infileName);
else sprintf(Line, "The graph ");
Message(Line);
switch (command)
{
case 'p' : sprintf(Line, "is%s planar.\n", Result==OK ? "" : " not"); break;
case 'd' : sprintf(Line, "is%s planar.\n", Result==OK ? "" : " not"); break;
case 'o' : sprintf(Line, "is%s outerplanar.\n", Result==OK ? "" : " not"); break;
case '2' : sprintf(Line, "has %s subgraph homeomorphic to K_{2,3}.\n", Result==OK ? "no" : "a"); break;
case '3' : sprintf(Line, "has %s subgraph homeomorphic to K_{3,3}.\n", Result==OK ? "no" : "a"); break;
case '4' : sprintf(Line, "has %s subgraph homeomorphic to K_4.\n", Result==OK ? "no" : "a"); break;
default : sprintf(Line, "has not been processed due to unrecognized command.\n"); break;
}
Message(Line);
sprintf(Line, "Algorithm '%s' executed in %.3lf seconds.\n",
GetAlgorithmName(command), platform_GetDuration(start,end));
Message(Line);
}
edge-addition-planarity-suite-Version_3.0.0.5/c/planarityUtils.c 0000664 0000000 0000000 00000025335 12716155161 0024636 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "planarity.h"
/****************************************************************************
Configuration
****************************************************************************/
char Mode='r',
OrigOut='n',
EmbeddableOut='n',
ObstructedOut='n',
AdjListsForEmbeddingsOut='n',
quietMode='n';
void Reconfigure()
{
fflush(stdin);
Prompt("\nDo you want to \n"
" Randomly generate graphs (r),\n"
" Specify a graph (s),\n"
" Randomly generate a maximal planar graph (m), or\n"
" Randomly generate a non-planar graph (n)?");
scanf(" %c", &Mode);
Mode = tolower(Mode);
if (!strchr("rsmn", Mode))
Mode = 's';
if (Mode == 'r')
{
Message("\nNOTE: The directories for the graphs you want must exist.\n\n");
Prompt("Do you want original graphs in directory 'random' (last 10 max)?");
scanf(" %c", &OrigOut);
Prompt("Do you want adj. matrix of embeddable graphs in directory 'embedded' (last 10 max))?");
scanf(" %c", &EmbeddableOut);
Prompt("Do you want adj. matrix of obstructed graphs in directory 'obstructed' (last 10 max)?");
scanf(" %c", &ObstructedOut);
Prompt("Do you want adjacency list format of embeddings in directory 'adjlist' (last 10 max)?");
scanf(" %c", &AdjListsForEmbeddingsOut);
}
FlushConsole(stdout);
}
/****************************************************************************
MESSAGE - prints a string, but when debugging adds \n and flushes stdout
****************************************************************************/
#define MAXLINE 1024
char Line[MAXLINE];
void Message(char *message)
{
if (quietMode == 'n')
{
fprintf(stdout, "%s", message);
#ifdef DEBUG
// fprintf(stdout, "\n");
fflush(stdout);
#endif
}
}
void ErrorMessage(char *message)
{
if (quietMode == 'n')
{
fprintf(stderr, "%s", message);
#ifdef DEBUG
fprintf(stderr, "\n");
fflush(stderr);
#endif
}
}
void FlushConsole(FILE *f)
{
#ifdef DEBUG
// Certain debuggers only flush completed lines of output to the console
fprintf(f, "\n");
#endif
fflush(f);
}
void Prompt(char *message)
{
Message(message);
FlushConsole(stdout);
}
/****************************************************************************
****************************************************************************/
void SaveAsciiGraph(graphP theGraph, char *filename)
{
int e, EsizeOccupied;
FILE *outfile = fopen(filename, "wt");
fprintf(outfile, "%s\n", filename);
EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2)
{
// Skip the edge holes
if (gp_EdgeInUse(theGraph, e))
{
fprintf(outfile, "%d %d\n", gp_GetNeighbor(theGraph, e)+1, gp_GetNeighbor(theGraph, e+1)+1);
}
}
fprintf(outfile, "0 0\n");
fclose(outfile);
}
/****************************************************************************
****************************************************************************/
int TextFilesEqual(char *file1Name, char *file2Name)
{
FILE *infile1 = NULL, *infile2 = NULL;
int Result = TRUE;
infile1 = fopen(file1Name, "r");
infile2 = fopen(file2Name, "r");
if (infile1 == NULL || infile2 == NULL)
Result = FALSE;
else
{
int c1=0, c2=0;
// Read the first file to the end
while ((c1 = fgetc(infile1)) != EOF)
{
// Want to suppress distinction between lines ending with CRLF versus LF
if (c1 == '\r')
continue;
// Get a char from the second file, except suppress CR again
while ((c2 = fgetc(infile2)) == '\r')
;
// If we got a char from the first file, but not from the second
// then the second file is shorter, so files are not equal
if (c2 == EOF)
{
Result = FALSE;
break;
}
// If we got a char from second file, but not equal to char from
// first file, then files are not equal
if (c1 != c2)
{
Result = FALSE;
break;
}
}
// If we got to the end of the first file without breaking the loop...
if (c1 == EOF)
{
// Then, once again, suppress CRs first, and then...
while ((c2 = fgetc(infile2)) == '\r')
;
// Test whether or not the second file also ends, same as the first.
if (fgetc(infile2) != EOF)
Result = FALSE;
}
}
if (infile1 != NULL) fclose(infile1);
if (infile2 != NULL) fclose(infile2);
return Result;
}
/****************************************************************************
****************************************************************************/
int BinaryFilesEqual(char *file1Name, char *file2Name)
{
FILE *infile1 = NULL, *infile2 = NULL;
int Result = TRUE;
infile1 = fopen(file1Name, "r");
infile2 = fopen(file2Name, "r");
if (infile1 == NULL || infile2 == NULL)
Result = FALSE;
else
{
int c1=0, c2=0;
// Read the first file to the end
while ((c1 = fgetc(infile1)) != EOF)
{
// If we got a char from the first file, but not from the second
// then the second file is shorter, so files are not equal
if ((c2 = fgetc(infile2)) == EOF)
{
Result = FALSE;
break;
}
// If we got a char from second file, but not equal to char from
// first file, then files are not equal
if (c1 != c2)
{
Result = FALSE;
break;
}
}
// If we got to the end of the first file without breaking the loop...
if (c1 == EOF)
{
// Then attempt to read from the second file to ensure it also ends.
if (fgetc(infile2) != EOF)
Result = FALSE;
}
}
if (infile1 != NULL) fclose(infile1);
if (infile2 != NULL) fclose(infile2);
return Result;
}
/****************************************************************************
****************************************************************************/
int GetEmbedFlags(char command)
{
int embedFlags = 0;
switch (command)
{
case 'o' : embedFlags = EMBEDFLAGS_OUTERPLANAR; break;
case 'p' : embedFlags = EMBEDFLAGS_PLANAR; break;
case 'd' : embedFlags = EMBEDFLAGS_DRAWPLANAR; break;
case '2' : embedFlags = EMBEDFLAGS_SEARCHFORK23; break;
case '3' : embedFlags = EMBEDFLAGS_SEARCHFORK33; break;
case '4' : embedFlags = EMBEDFLAGS_SEARCHFORK4; break;
}
return embedFlags;
}
/****************************************************************************
****************************************************************************/
char *GetAlgorithmName(char command)
{
char *algorithmName = "UnsupportedAlgorithm";
switch (command)
{
case 'p' : algorithmName = "PlanarEmbed"; break;
case 'd' : algorithmName = DRAWPLANAR_NAME; break;
case 'o' : algorithmName = "OuterplanarEmbed"; break;
case '2' : algorithmName = K23SEARCH_NAME; break;
case '3' : algorithmName = K33SEARCH_NAME; break;
case '4' : algorithmName = K4SEARCH_NAME; break;
}
return algorithmName;
}
/****************************************************************************
****************************************************************************/
void AttachAlgorithm(graphP theGraph, char command)
{
switch (command)
{
case 'd' : gp_AttachDrawPlanar(theGraph); break;
case '2' : gp_AttachK23Search(theGraph); break;
case '3' : gp_AttachK33Search(theGraph); break;
case '4' : gp_AttachK4Search(theGraph); break;
}
}
/****************************************************************************
A string used to construct input and output filenames.
The SUFFIXMAXLENGTH is 32 to accommodate ".out.txt" + ".render.txt" + ".test.txt"
****************************************************************************/
#define FILENAMEMAXLENGTH 128
#define ALGORITHMNAMEMAXLENGTH 32
#define SUFFIXMAXLENGTH 32
char theFileName[FILENAMEMAXLENGTH+1+ALGORITHMNAMEMAXLENGTH+1+SUFFIXMAXLENGTH+1];
/****************************************************************************
ConstructInputFilename()
Returns a string not owned by the caller (do not free string).
String contains infileName content if infileName is non-NULL.
If infileName is NULL, then the user is asked to supply a name.
Returns NULL on error, or a non-NULL string on success.
****************************************************************************/
char *ConstructInputFilename(char *infileName)
{
if (infileName == NULL)
{
Prompt("Enter graph file name: ");
fflush(stdin);
scanf(" %s", theFileName);
if (!strchr(theFileName, '.'))
strcat(theFileName, ".txt");
}
else
{
if (strlen(infileName) > FILENAMEMAXLENGTH)
{
ErrorMessage("Filename is too long");
return NULL;
}
strcpy(theFileName, infileName);
}
return theFileName;
}
/****************************************************************************
ConstructPrimaryOutputFilename()
Returns a string not owned by the caller (do not free string).
Reuses the same memory space as ConstructinputFilename().
If outfileName is non-NULL, then the result string contains its content.
If outfileName is NULL, then the infileName and the command's algorithm name
are used to construct a string.
Returns non-NULL string
****************************************************************************/
char *ConstructPrimaryOutputFilename(char *infileName, char *outfileName, char command)
{
char *algorithmName = GetAlgorithmName(command);
if (outfileName == NULL)
{
// The output filename is based on the input filename
if (theFileName != infileName)
strcpy(theFileName, infileName);
// If the primary output filename has not been given, then we use
// the input filename + the algorithm name + a simple suffix
if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH)
{
strcat(theFileName, ".");
strcat(theFileName, algorithmName);
}
else
ErrorMessage("Algorithm Name is too long, so it will not be used in output filename.");
strcat(theFileName, ".out.txt");
}
else
{
if (strlen(outfileName) > FILENAMEMAXLENGTH)
{
// The output filename is based on the input filename
if (theFileName != infileName)
strcpy(theFileName, infileName);
if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH)
{
strcat(theFileName, ".");
strcat(theFileName, algorithmName);
}
strcat(theFileName, ".out.txt");
sprintf(Line, "Outfile filename is too long. Result placed in %s", theFileName);
ErrorMessage(Line);
}
else
{
if (theFileName != outfileName)
strcpy(theFileName, outfileName);
}
}
return theFileName;
}
edge-addition-planarity-suite-Version_3.0.0.5/c/platformTime.h 0000664 0000000 0000000 00000003012 12716155161 0024246 0 ustar 00root root 0000000 0000000 #ifndef PLATFORM_TIME
#define PLATFORM_TIME
/*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifdef WIN32
#include
#include
#define platform_time DWORD
#define platform_GetTime(timeVar) (timeVar = GetTickCount())
#define platform_GetDuration(startTime, endTime) ((double) (endTime-startTime) / 1000.0)
#else
#include
typedef struct {
clock_t hiresTime;
time_t lowresTime;
} platform_time;
#define platform_GetTime(timeVar) (timeVar.hiresTime = clock(), timeVar.lowresTime = time(NULL))
// Many flavors of Unix have CLOCKS_PER_SEC at 1 million, and clock_t as a 4 byte long integer
// which means that the clock() construct has a resolution of only about 2000 seconds
// If we're getting a duration longer than that, then we fall back to the coarser time() measure
#define platform_GetDuration(startTime, endTime) ( \
( (double) (endTime.lowresTime - startTime.lowresTime) ) > 2000 ? \
( (double) (endTime.lowresTime - startTime.lowresTime) ) : \
( (double) (endTime.hiresTime - startTime.hiresTime)) / CLOCKS_PER_SEC)
/*
#define platform_time clock_t
#define platform_GetTime() clock()
#define platform_GetDuration(startTime, endTime) (((double) (endTime - startTime)) / CLOCKS_PER_SEC)
*/
/*
#define platform_time time_t
#define platform_GetTime() time((time_t *)NULL)
#define platform_GetDuration(startTime, endTime) ((double) (endTime - startTime))
*/
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/ 0000775 0000000 0000000 00000000000 12716155161 0023102 5 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.0-based.txt 0000664 0000000 0000000 00000000210 12716155161 0026633 0 ustar 00root root 0000000 0000000 N=10
0: 1 5 4 -1
1: 2 6 0 -1
2: 3 7 1 -1
3: 4 8 2 -1
4: 0 9 3 -1
5: 0 7 8 -1
6: 1 8 9 -1
7: 2 9 5 -1
8: 3 5 6 -1
9: 4 6 7 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.0-based.txt.ColorVertices.out.txt 0000664 0000000 0000000 00000000347 12716155161 0033054 0 ustar 00root root 0000000 0000000 N=10
0: 1 5 4 -1
1: 2 6 0 -1
2: 3 7 1 -1
3: 4 8 2 -1
4: 0 9 3 -1
5: 0 7 8 -1
6: 1 8 9 -1
7: 2 9 5 -1
8: 3 5 6 -1
9: 4 6 7 -1
0: 0
1: 2
2: 0
3: 1
4: 2
5: 2
6: 1
7: 1
8: 0
9: 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.0-based.txt.K23Search.out.txt 0000664 0000000 0000000 00000000154 12716155161 0031752 0 ustar 00root root 0000000 0000000 N=10
0: -1
1: -1
2: -1
3: 4 8 -1
4: 3 9 -1
5: 7 8 -1
6: 8 9 -1
7: 9 5 -1
8: 5 6 3 -1
9: 4 6 7 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.0-based.txt.K33Search.out.txt 0000664 0000000 0000000 00000000174 12716155161 0031755 0 ustar 00root root 0000000 0000000 N=10
0: -1
1: 2 6 -1
2: 1 3 7 -1
3: 2 4 8 -1
4: 3 9 -1
5: 7 8 -1
6: 8 9 1 -1
7: 9 5 2 -1
8: 5 6 3 -1
9: 4 6 7 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.0-based.txt.K4Search.out.txt 0000664 0000000 0000000 00000000164 12716155161 0031672 0 ustar 00root root 0000000 0000000 N=10
0: -1
1: -1
2: 3 7 -1
3: 2 4 8 -1
4: 3 9 -1
5: 7 8 -1
6: 8 9 -1
7: 9 5 2 -1
8: 5 6 3 -1
9: 4 6 7 -1
Petersen.0-based.txt.OuterplanarEmbed.out.txt 0000664 0000000 0000000 00000000154 12716155161 0033437 0 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/samples N=10
0: -1
1: -1
2: -1
3: 4 8 -1
4: 3 9 -1
5: 7 8 -1
6: 8 9 -1
7: 9 5 -1
8: 5 6 3 -1
9: 4 6 7 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.0-based.txt.PlanarEmbed.out.txt 0000664 0000000 0000000 00000000174 12716155161 0032441 0 ustar 00root root 0000000 0000000 N=10
0: -1
1: 2 6 -1
2: 1 3 7 -1
3: 2 4 8 -1
4: 3 9 -1
5: 7 8 -1
6: 8 9 1 -1
7: 9 5 2 -1
8: 5 6 3 -1
9: 4 6 7 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt 0000664 0000000 0000000 00000000202 12716155161 0025422 0 ustar 00root root 0000000 0000000 N=10
1: 2 6 5 0
2: 3 7 1 0
3: 4 8 2 0
4: 5 9 3 0
5: 1 10 4 0
6: 1 8 9 0
7: 2 9 10 0
8: 3 10 6 0
9: 4 6 7 0
10: 5 7 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt.ColorVertices.out.txt 0000664 0000000 0000000 00000000342 12716155161 0031635 0 ustar 00root root 0000000 0000000 N=10
1: 2 6 5 0
2: 3 7 1 0
3: 4 8 2 0
4: 5 9 3 0
5: 1 10 4 0
6: 1 8 9 0
7: 2 9 10 0
8: 3 10 6 0
9: 4 6 7 0
10: 5 7 8 0
1: 0
2: 2
3: 0
4: 1
5: 2
6: 2
7: 1
8: 1
9: 0
10: 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt.K23Search.out.txt 0000664 0000000 0000000 00000000146 12716155161 0030541 0 ustar 00root root 0000000 0000000 N=10
1: 0
2: 0
3: 0
4: 5 9 0
5: 4 10 0
6: 8 9 0
7: 9 10 0
8: 10 6 0
9: 6 7 4 0
10: 5 7 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt.K33Search.out.txt 0000664 0000000 0000000 00000000166 12716155161 0030544 0 ustar 00root root 0000000 0000000 N=10
1: 0
2: 3 7 0
3: 2 4 8 0
4: 3 5 9 0
5: 4 10 0
6: 8 9 0
7: 9 10 2 0
8: 10 6 3 0
9: 6 7 4 0
10: 5 7 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt.K4Search.out.txt 0000664 0000000 0000000 00000000156 12716155161 0030461 0 ustar 00root root 0000000 0000000 N=10
1: 0
2: 0
3: 4 8 0
4: 3 5 9 0
5: 4 10 0
6: 8 9 0
7: 9 10 0
8: 10 6 3 0
9: 6 7 4 0
10: 5 7 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt.OuterplanarEmbed.out.txt 0000664 0000000 0000000 00000000146 12716155161 0032305 0 ustar 00root root 0000000 0000000 N=10
1: 0
2: 0
3: 0
4: 5 9 0
5: 4 10 0
6: 8 9 0
7: 9 10 0
8: 10 6 0
9: 6 7 4 0
10: 5 7 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/Petersen.txt.PlanarEmbed.out.txt 0000664 0000000 0000000 00000000166 12716155161 0031230 0 ustar 00root root 0000000 0000000 N=10
1: 0
2: 3 7 0
3: 2 4 8 0
4: 3 5 9 0
5: 4 10 0
6: 8 9 0
7: 9 10 2 0
8: 10 6 3 0
9: 6 7 4 0
10: 5 7 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/drawExample.0-based.txt 0000664 0000000 0000000 00000000332 12716155161 0027324 0 ustar 00root root 0000000 0000000 N=15
0: 1 3 14 -4
1: 0 2 5 8 11 -4
2: 1 11 5 -4
3: 0 4 7 -4
4: 3 6 10 12 -4
5: 1 9 2 -4
6: 4 13 -4
7: 3 10 14 -4
8: 1 13 -4
9: 5 14 -4
10: 4 12 7 -4
11: 1 2 -4
12: 4 10 13 -4
13: 8 12 6 -4
14: 0 9 7 -4
drawExample.0-based.txt.ColorVertices.out.txt 0000664 0000000 0000000 00000000534 12716155161 0033457 0 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/samples N=15
0: 1 3 14 -1
1: 0 2 5 8 11 -1
2: 1 11 5 -1
3: 0 4 7 -1
4: 3 6 10 12 -1
5: 1 9 2 -1
6: 4 13 -1
7: 3 10 14 -1
8: 1 13 -1
9: 5 14 -1
10: 4 12 7 -1
11: 1 2 -1
12: 4 10 13 -1
13: 8 12 6 -1
14: 0 9 7 -1
0: 1
1: 0
2: 1
3: 0
4: 1
5: 2
6: 2
7: 1
8: 1
9: 1
10: 0
11: 2
12: 2
13: 0
14: 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/drawExample.0-based.txt.DrawPlanar.out.txt 0000664 0000000 0000000 00000001670 12716155161 0033010 0 ustar 00root root 0000000 0000000 N=15
0: 1 3 14 -1
1: 2 11 5 8 0 -1
2: 5 11 1 -1
3: 0 4 7 -1
4: 6 12 10 3 -1
5: 9 1 2 -1
6: 13 4 -1
7: 3 10 14 -1
8: 1 13 -1
9: 14 5 -1
10: 7 4 12 -1
11: 1 2 -1
12: 10 4 13 -1
13: 8 12 6 -1
14: 0 7 9 -1
0: 0 0 21
1: 14 4 21
2: 12 3 5
3: 1 8 11
4: 2 11 16
5: 11 2 7
6: 3 16 17
7: 8 9 13
8: 7 19 20
9: 10 1 2
10: 6 12 15
11: 13 5 6
12: 5 14 18
13: 4 17 19
14: 9 0 10
0: 21 0 14
1: 21 0 14
2: 8 0 1
3: 8 0 1
4: 0 0 9
5: 0 0 9
6: 4 12 14
7: 4 12 14
8: 7 11 14
9: 7 11 14
10: 20 7 14
11: 20 7 14
12: 6 13 14
13: 6 13 14
14: 5 12 13
15: 5 12 13
16: 3 11 12
17: 3 11 12
18: 11 1 2
19: 11 1 2
20: 9 1 8
21: 9 1 8
22: 16 2 3
23: 16 2 3
24: 12 2 6
25: 12 2 6
26: 14 2 5
27: 14 2 5
28: 2 10 11
29: 2 10 11
30: 17 3 4
31: 17 3 4
32: 13 6 8
33: 13 6 8
34: 10 8 9
35: 10 8 9
36: 19 4 7
37: 19 4 7
38: 1 9 10
39: 1 9 10
40: 15 5 6
41: 15 5 6
42: 18 4 5
43: 18 4 5
drawExample.0-based.txt.DrawPlanar.out.txt.render.txt 0000664 0000000 0000000 00000001320 12716155161 0035015 0 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/samples ----------0-----------
| | |
| -3-- |
| | | |
| | --4--- |
| | | | | |
| | | | 6- |
| | | | | |
| | | | -13 |
| | | | || |
| | | --12-| |
| | | | | |
| | -10- | |
| | | | |
| | | 8-|
| | | ||
| --7-- ||
| | ||
-----14---- ||
| ||
9- ||
| ||
--5--- ||
| | ||
-2- | ||
|| | ||
|11| ||
| || ||
--------1---------
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/drawExample.txt 0000664 0000000 0000000 00000000316 12716155161 0026114 0 ustar 00root root 0000000 0000000 N=15
1: 2 4 15 0
2: 1 3 6 9 12 0
3: 2 12 6 0
4: 1 5 8 0
5: 4 7 11 13 0
6: 2 10 3 0
7: 5 14 0
8: 4 11 15 0
9: 2 14 0
10: 6 15 0
11: 5 13 8 0
12: 2 3 0
13: 5 11 14 0
14: 9 13 7 0
15: 1 10 8 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/drawExample.txt.ColorVertices.out.txt 0000664 0000000 0000000 00000000521 12716155161 0032320 0 ustar 00root root 0000000 0000000 N=15
1: 2 4 15 0
2: 1 3 6 9 12 0
3: 2 12 6 0
4: 1 5 8 0
5: 4 7 11 13 0
6: 2 10 3 0
7: 5 14 0
8: 4 11 15 0
9: 2 14 0
10: 6 15 0
11: 5 13 8 0
12: 2 3 0
13: 5 11 14 0
14: 9 13 7 0
15: 1 10 8 0
1: 1
2: 0
3: 1
4: 0
5: 1
6: 2
7: 2
8: 1
9: 1
10: 1
11: 0
12: 2
13: 2
14: 0
15: 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/drawExample.txt.DrawPlanar.out.txt 0000664 0000000 0000000 00000001657 12716155161 0031603 0 ustar 00root root 0000000 0000000 N=15
1: 2 4 15 0
2: 3 12 6 9 1 0
3: 6 12 2 0
4: 1 5 8 0
5: 7 13 11 4 0
6: 10 2 3 0
7: 14 5 0
8: 4 11 15 0
9: 2 14 0
10: 15 6 0
11: 8 5 13 0
12: 2 3 0
13: 11 5 14 0
14: 9 13 7 0
15: 1 8 10 0
1: 0 0 21
2: 14 4 21
3: 12 3 5
4: 1 8 11
5: 2 11 16
6: 11 2 7
7: 3 16 17
8: 8 9 13
9: 7 19 20
10: 10 1 2
11: 6 12 15
12: 13 5 6
13: 5 14 18
14: 4 17 19
15: 9 0 10
2: 21 0 14
3: 21 0 14
4: 8 0 1
5: 8 0 1
6: 0 0 9
7: 0 0 9
8: 4 12 14
9: 4 12 14
10: 7 11 14
11: 7 11 14
12: 20 7 14
13: 20 7 14
14: 6 13 14
15: 6 13 14
16: 5 12 13
17: 5 12 13
18: 3 11 12
19: 3 11 12
20: 11 1 2
21: 11 1 2
22: 9 1 8
23: 9 1 8
24: 16 2 3
25: 16 2 3
26: 12 2 6
27: 12 2 6
28: 14 2 5
29: 14 2 5
30: 2 10 11
31: 2 10 11
32: 17 3 4
33: 17 3 4
34: 13 6 8
35: 13 6 8
36: 10 8 9
37: 10 8 9
38: 19 4 7
39: 19 4 7
40: 1 9 10
41: 1 9 10
42: 15 5 6
43: 15 5 6
44: 18 4 5
45: 18 4 5
drawExample.txt.DrawPlanar.out.txt.render.txt 0000664 0000000 0000000 00000001320 12716155161 0033603 0 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/samples ----------1-----------
| | |
| -4-- |
| | | |
| | --5--- |
| | | | | |
| | | | 7- |
| | | | | |
| | | | -14 |
| | | | || |
| | | --13-| |
| | | | | |
| | -11- | |
| | | | |
| | | 9-|
| | | ||
| --8-- ||
| | ||
-----15---- ||
| ||
10 ||
| ||
--6--- ||
| | ||
-3- | ||
|| | ||
|12| ||
| || ||
--------2---------
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.0-based.txt 0000664 0000000 0000000 00000000114 12716155161 0027061 0 ustar 00root root 0000000 0000000 N=5
0: 2 1 4 3 -1
1: 2 4 0 3 -1
2: 0 1 4 -1
3: 4 0 1 -1
4: 1 0 3 2 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.0-based.txt.ColorVertices.out.txt0000664 0000000 0000000 00000000215 12716155161 0033271 0 ustar 00root root 0000000 0000000 N=5
0: 2 1 4 3 -1
1: 2 4 0 3 -1
2: 0 1 4 -1
3: 4 0 1 -1
4: 1 0 3 2 -1
0: 1
1: 2
2: 3
3: 3
4: 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.0-based.txt.DrawPlanar.out.txt 0000664 0000000 0000000 00000000527 12716155161 0032547 0 ustar 00root root 0000000 0000000 N=5
0: 2 4 3 1 -1
1: 0 3 4 2 -1
2: 1 4 0 -1
3: 0 4 1 -1
4: 0 2 1 3 -1
0: 0 0 8
1: 3 0 6
2: 4 1 8
3: 2 2 5
4: 1 4 7
0: 8 0 4
1: 8 0 4
2: 0 0 3
3: 0 0 3
4: 4 0 1
5: 4 0 1
6: 2 0 2
7: 2 0 2
8: 1 3 4
9: 1 3 4
10: 6 1 3
11: 6 1 3
12: 3 2 3
13: 3 2 3
14: 7 1 4
15: 7 1 4
16: 5 1 2
17: 5 1 2
maxPlanar5.0-based.txt.DrawPlanar.out.txt.render.txt 0000664 0000000 0000000 00000000156 12716155161 0034562 0 ustar 00root root 0000000 0000000 edge-addition-planarity-suite-Version_3.0.0.5/c/samples ----0----
| | | |
| | -4--|
| | ||||
| -3--|||
| | |||
---1---||
| ||
---2----
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.0-based.txt.PlanarEmbed.out.txt 0000664 0000000 0000000 00000000114 12716155161 0032656 0 ustar 00root root 0000000 0000000 N=5
0: 2 4 3 1 -1
1: 0 3 4 2 -1
2: 1 4 0 -1
3: 0 4 1 -1
4: 0 2 1 3 -1
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.txt 0000664 0000000 0000000 00000000107 12716155161 0025651 0 ustar 00root root 0000000 0000000 N=5
1: 3 2 5 4 0
2: 3 5 1 4 0
3: 1 2 5 0
4: 5 1 2 0
5: 2 1 4 3 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.txt.ColorVertices.out.txt 0000664 0000000 0000000 00000000210 12716155161 0032052 0 ustar 00root root 0000000 0000000 N=5
1: 3 2 5 4 0
2: 3 5 1 4 0
3: 1 2 5 0
4: 5 1 2 0
5: 2 1 4 3 0
1: 1
2: 2
3: 3
4: 3
5: 0
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.txt.DrawPlanar.out.txt 0000664 0000000 0000000 00000000524 12716155161 0031332 0 ustar 00root root 0000000 0000000 N=5
1: 3 5 4 2 0
2: 1 4 5 3 0
3: 2 5 1 0
4: 1 5 2 0
5: 1 3 2 4 0
1: 0 0 8
2: 3 0 6
3: 4 1 8
4: 2 2 5
5: 1 4 7
2: 8 0 4
3: 8 0 4
4: 0 0 3
5: 0 0 3
6: 4 0 1
7: 4 0 1
8: 2 0 2
9: 2 0 2
10: 1 3 4
11: 1 3 4
12: 6 1 3
13: 6 1 3
14: 3 2 3
15: 3 2 3
16: 7 1 4
17: 7 1 4
18: 5 1 2
19: 5 1 2
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.txt.DrawPlanar.out.txt.render.txt0000664 0000000 0000000 00000000156 12716155161 0033427 0 ustar 00root root 0000000 0000000 ----1----
| | | |
| | -5--|
| | ||||
| -4--|||
| | |||
---2---||
| ||
---3----
edge-addition-planarity-suite-Version_3.0.0.5/c/samples/maxPlanar5.txt.PlanarEmbed.out.txt 0000664 0000000 0000000 00000000107 12716155161 0031446 0 ustar 00root root 0000000 0000000 N=5
1: 3 5 4 2 0
2: 1 4 5 3 0
3: 2 5 1 0
4: 1 5 2 0
5: 1 3 2 4 0
edge-addition-planarity-suite-Version_3.0.0.5/c/stack.c 0000664 0000000 0000000 00000007403 12716155161 0022713 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#include "appconst.h"
#include "stack.h"
#include
stackP sp_New(int capacity)
{
stackP theStack;
theStack = (stackP) malloc(sizeof(stack));
if (theStack != NULL)
{
theStack->S = (int *) malloc(capacity*sizeof(int));
if (theStack->S == NULL)
{
free(theStack);
theStack = NULL;
}
}
if (theStack != NULL)
{
theStack->capacity = capacity;
sp_ClearStack(theStack);
}
return theStack;
}
void sp_Free(stackP *pStack)
{
if (pStack == NULL || *pStack == NULL) return;
(*pStack)->capacity = (*pStack)->size = 0;
if ((*pStack)->S != NULL)
free((*pStack)->S);
(*pStack)->S = NULL;
free(*pStack);
*pStack = NULL;
}
int sp_CopyContent(stackP stackDst, stackP stackSrc)
{
if (stackDst->capacity < stackSrc->size)
return NOTOK;
if (stackSrc->size > 0)
memcpy(stackDst->S, stackSrc->S, stackSrc->size*sizeof(int));
stackDst->size = stackSrc->size;
return OK;
}
stackP sp_Duplicate(stackP theStack)
{
stackP newStack = sp_New(theStack->capacity);
if (newStack == NULL)
return NULL;
if (theStack->size > 0)
{
memcpy(newStack->S, theStack->S, theStack->size*sizeof(int));
newStack->size = theStack->size;
}
return newStack;
}
int sp_Copy(stackP stackDst, stackP stackSrc)
{
if (sp_CopyContent(stackDst, stackSrc) != OK)
{
stackP newStack = sp_Duplicate(stackSrc);
int *p;
if (newStack == NULL)
return NOTOK;
p = stackDst->S;
stackDst->S = newStack->S;
newStack->S = p;
newStack->capacity = stackDst->capacity;
sp_Free(&newStack);
stackDst->size = stackSrc->size;
stackDst->capacity = stackSrc->capacity;
}
return OK;
}
#ifndef SPEED_MACROS
int sp_ClearStack(stackP theStack)
{
theStack->size = 0;
return OK;
}
int sp_GetCurrentSize(stackP theStack)
{
return theStack->size;
}
int sp_SetCurrentSize(stackP theStack, int size)
{
return size > theStack->capacity ? NOTOK : (theStack->size = size, OK);
}
int sp_IsEmpty(stackP theStack)
{
return !theStack->size;
}
int sp_NonEmpty(stackP theStack)
{
return theStack->size;
}
int sp__Push(stackP theStack, int a)
{
if (theStack->size >= theStack->capacity)
return NOTOK;
theStack->S[theStack->size++] = a;
return OK;
}
int sp__Push2(stackP theStack, int a, int b)
{
if (theStack->size + 1 >= theStack->capacity)
return NOTOK;
theStack->S[theStack->size++] = a;
theStack->S[theStack->size++] = b;
return OK;
}
int sp__Pop(stackP theStack, int *pA)
{
if (theStack->size <= 0)
return NOTOK;
*pA = theStack->S[--theStack->size];
return OK;
}
int sp__Pop2(stackP theStack, int *pA, int *pB)
{
if (theStack->size <= 1)
return NOTOK;
*pB = theStack->S[--theStack->size];
*pA = theStack->S[--theStack->size];
return OK;
}
int sp_Top(stackP theStack)
{
return theStack->size ? theStack->S[theStack->size-1] : NIL;
}
int sp_Get(stackP theStack, int pos)
{
if (theStack == NULL || pos < 0 || pos >= theStack->size)
return NOTOK;
return (theStack->S[pos]);
}
int sp_Set(stackP theStack, int pos, int val)
{
if (theStack == NULL || pos < 0 || pos >= theStack->size)
return NOTOK;
return (theStack->S[pos] = val);
}
#endif // not defined SPEED_MACROS
edge-addition-planarity-suite-Version_3.0.0.5/c/stack.h 0000664 0000000 0000000 00000004273 12716155161 0022722 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 1997-2015, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
#ifndef STACK_H
#define STACK_H
#ifdef __cplusplus
extern "C" {
#endif
// includes mem functions like memcpy
#include
typedef struct
{
int *S;
int size, capacity;
} stack;
typedef stack * stackP;
stackP sp_New(int);
void sp_Free(stackP *);
int sp_Copy(stackP, stackP);
int sp_CopyContent(stackP stackDst, stackP stackSrc);
stackP sp_Duplicate(stackP theStack);
#define sp_GetCapacity(theStack) (theStack->capacity)
#ifndef SPEED_MACROS
int sp_ClearStack(stackP);
int sp_GetCurrentSize(stackP theStack);
int sp_SetCurrentSize(stackP theStack, int top);
int sp_IsEmpty(stackP);
int sp_NonEmpty(stackP);
#define sp_Push(theStack, a) { if (sp__Push(theStack, (a)) != OK) return NOTOK; }
#define sp_Push2(theStack, a, b) { if (sp__Push2(theStack, (a), (b)) != OK) return NOTOK; }
int sp__Push(stackP, int);
int sp__Push2(stackP, int, int);
#define sp_Pop(theStack, a) { if (sp__Pop(theStack, &(a)) != OK) return NOTOK; }
#define sp_Pop2(theStack, a, b) { if (sp__Pop2(theStack, &(a), &(b)) != OK) return NOTOK; }
int sp__Pop(stackP, int *);
int sp__Pop2(stackP, int *, int *);
int sp_Top(stackP);
int sp_Get(stackP, int);
int sp_Set(stackP, int, int);
#else
#define sp_ClearStack(theStack) theStack->size=0
#define sp_GetCurrentSize(theStack) (theStack->size)
#define sp_SetCurrentSize(theStack, Size) ((Size) > theStack->capacity ? NOTOK : (theStack->size = (Size), OK))
#define sp_IsEmpty(theStack) !theStack->size
#define sp_NonEmpty(theStack) theStack->size
#define sp_Push(theStack, a) theStack->S[theStack->size++] = a
#define sp_Push2(theStack, a, b) {sp_Push(theStack, a); sp_Push(theStack, b);}
#define sp_Pop(theStack, a) a=theStack->S[--theStack->size]
#define sp_Pop2(theStack, a, b) {sp_Pop(theStack, b);sp_Pop(theStack, a);}
#define sp_Top(theStack) (theStack->size ? theStack->S[theStack->size-1] : NIL)
#define sp_Get(theStack, pos) (theStack->S[pos])
#define sp_Set(theStack, pos, val) (theStack->S[pos] = val)
#endif
#ifdef __cplusplus
}
#endif
#endif
edge-addition-planarity-suite-Version_3.0.0.5/configure.ac 0000664 0000000 0000000 00000001555 12716155161 0023510 0 ustar 00root root 0000000 0000000 AC_INIT(planarity, 3.0.0.5, jboyer@acm.org)
AM_INIT_AUTOMAKE([subdir-objects] [foreign])
AC_CONFIG_SRCDIR([c/])
# The version of the libtool library is of the form current:revision:age
#
# See http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
#
# When doing a release, they should be updated like this:
# 1. If no interfaces changed, only implementations: just increment
# revision.
# 2. If interfaces were added, none removed: increment current, set
# revision to zero and increment age.
# 3. If interfaces were removed (breaks backward compatibility): increment
# current, and set both revision and age to zero.
LT_CURRENT=0
LT_REVISION=0
LT_AGE=0
AC_SUBST(LT_CURRENT)
AC_SUBST(LT_REVISION)
AC_SUBST(LT_AGE)
AC_PROG_CC
AC_PROG_LIBTOOL
AC_PROG_INSTALL
AC_CHECK_HEADERS([ctype.h stdio.h stdlib.h string.h time.h unistd.h])
AC_OUTPUT(Makefile)
edge-addition-planarity-suite-Version_3.0.0.5/planarity.1 0000664 0000000 0000000 00000004236 12716155161 0023306 0 ustar 00root root 0000000 0000000 .TH planarity 1
.SH NAME
planarity - The Edge Addition Planarity Suite
.SH SYNOPSIS
.B planarity
.B [-q]
.B [-h|-help]
.B [-i|-info]
.B [-r]
.B [-s]
.B [-rm]
.B [-rn]
.SH DESCRIPTION
Invokes the Edge Addition Planarity Suite commandline tool.
Without argument, the tool presents a menu-driven interactive
interface.
When used in batch mode, the tool returns 0 for a planar graph, 1 for
a nonplanar graph and -1 on error.
.SH OPTIONS
.TP
.B -q
Quiet mode
.TP
.B -h, -help
Display some help
.TP
.B -i, -info
Display copyright, license and reference articles information
.SH FEATURES
Features in batch mode.
.TP
.B -r C K N
Run the C command (see below) on K random graphs with N vertices
.TP
.B -s C I O [O2]
Run the C command (see below) on a specific graph given in the I input
file, with output in the primary O file and complementary information
in the secondary O2 file.
.TP
.B -rm N O [O2]
Compute a maximal planar random graph on N vertices with output in the
primary O file and optionally the chosen graph in the O2 file.
.TP
.B -rn N O [O2]
Compute a nonplanar random graph (maximal planar and an edge) on N
vertices with output in the primary O file and optionally the chosen
graph in the O2 file.
.SH COMMANDS
Determine which algorithm implementation to run
.TP
.B -p
Planar embedding and Kuratowski subgraph isolation
.TP
.B -d
Planar graph drawing by visibility representation
.TP
.B -o
Outerplanar embedding and obstruction isolation
.TP
.B -2
Search for subgraph homeomorphic to K_{2,3}
.TP
.B -3
Search for subgraph homeomorphic to K_{3,3}
.TP
.B -4
Search for subgraph homeomorphic to K_4
.TP
.B -a
All of the above
.SH EXAMPLES
.TP
.B planarity -s -q -p infile.txt embedding.out [obstruction.out]
Process infile.txt in quiet mode (-q), putting planar embedding in
embedding.out or (optionally) a Kuratowski subgraph in Obstruction.out
Process returns 0=planar, 1=nonplanar, -1=error
.TP
.B planarity -s -q -d infile.txt embedding.out [drawing.out]
If graph in infile.txt is planar, then put embedding in embedding.out
and (optionally) an ASCII art drawing in drawing.out
Process returns 0=planar, 1=nonplanar, -1=error
.SH SEE ALSO
\fBplanarity -h -menu\fR for more information.