pax_global_header00006660000000000000000000000064142045050370014512gustar00rootroot0000000000000052 comment=0def65ade3624fc69af3d53bf44421a628635372 edge-addition-planarity-suite-Version_3.0.2.0/000077500000000000000000000000001420450503700212045ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/.gitignore000066400000000000000000000016611420450503700232000ustar00rootroot00000000000000# Directories /c/Release/ /c/Debug/ /c/.settings/ /c/error/ /c/random/ /c/embedded/ /c/adjlist/ /c/obstructed/ /c/Release/random/ /c/Release/embedded/ /c/Release/adjlist/ /c/Release/obstructed/ /c/Release/error/ /c/Debug/random/ /c/Debug/embedded/ /c/Debug/adjlist/ /c/Debug/obstructed/ /c/Debug/error/ # 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/ # Java *.class *.log *.jar *.war *.ear *.zip *.tar.gz *.rar # To ignore files generated by the make distribution process *.tar.gz /c/.deps/ /autom4te.cache/ libtool test-samples.sh Makefile config.status config.guess config.sub depcomp install-sh Makefile.in missing ltmain.sh configure aclocal.m4 compile edge-addition-planarity-suite-Version_3.0.2.0/.project000066400000000000000000000003331420450503700226520ustar00rootroot00000000000000 Planarity-trunk edge-addition-planarity-suite-Version_3.0.2.0/LICENSE.TXT000066400000000000000000000051251420450503700226720ustar00rootroot00000000000000The Edge Addition Planarity Suite Copyright (c) 1997-2022, 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://dx.doi.org/10.7155/jgaa.00268 * 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. http://dx.doi.org/10.1007/11618058_47 * 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://dx.doi.org/10.7155/jgaa.00091 * John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, University of Victoria, 2001. https://dspace.library.uvic.ca/handle/1828/9918 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.2.0/Makefile.am000066400000000000000000000027141420450503700232440ustar00rootroot00000000000000SUBDIRS = c/samples 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 \ c/strbuf.c libplanarity_la_LDFLAGS = -no-undefined $(AM_LDFLAGS) -version-info @LT_CURRENT@:@LT_REVISION@:@LT_AGE@ pkginclude_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 \ c/strbuf.h bin_PROGRAMS = planarity planarity_LDADD = libplanarity.la planarity_SOURCES = c/planarity.c c/planarityCommandLine.c man1_MANS = planarity.1 EXTRA_DIST=$(man1_MANS) TESTS = test-samples.sh # The @docdir@ variable will be replaced by the ./configure script. docdir = @docdir@ dist_doc_DATA = README.md LICENSE.TXT edge-addition-planarity-suite-Version_3.0.2.0/README.md000066400000000000000000000233611420450503700224700ustar00rootroot00000000000000# Edge Addition Planarity Suite The primary purpose of this repository is to provide implementations of the edge addition planar graph embedding algorithm and related algorithms, including a planar graph drawing method, an isolator for a minimal subgraph obstructing planarity in non-planar graphs, outerplanar graph embedder and obstruction isolator algorithms, and tester/isolator algorithms for subgraphs homeomorphic to _K2,3_, _K4_, and _K3,3_. The C implementations in this repository are the reference implementations of algorithms appearing in the following papers: * [Subgraph Homeomorphism via the Edge Addition Planarity Algorithm](http://dx.doi.org/10.7155/jgaa.00268) * [A New Method for Efficiently Generating Planar Graph Visibility Representations](http://dx.doi.org/10.1007/11618058_47) * [On the Cutting Edge: Simplified O(n) Planarity by Edge Addition](http://dx.doi.org/10.7155/jgaa.00091) * [Simplified O(n) Algorithms for Planar Graph Embedding, Kuratowski Subgraph Isolation, and Related Problems](https://dspace.library.uvic.ca/handle/1828/9918) As secondary purpose of this repository is to provide a generalized graph API that enables implementation of a very wide range of in-memory graph algorithms including basic methods for reading, writing, depth first search, and lowpoint as well as advanced methods for solving planarity, outerplanarity, drawing, and selected subgraph homeomorphism problems. An extension mechanism is also provided to enable implementation of planarity-related algorithms by overriding and augmenting data structures and methods of the core planarity algorithm. ## Getting Started These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ### Pre-compiled Executable Releases for Non-Developers On several distributions of Linux, you may be able to get the planarity executable with _sudo apt-get planarity_ , or you may already have it you have Matlab. For non-developer Windows users, there is also a pre-compiled executable version of the algorithm implementations. Download and decompress the _planarity-N.N.N.N.WindowsExe.zip_ file. If you run the _planarity_ executable program, it will offer an interactive, menu-driven mode that lets a user manually select algorithms to run and, where appropriate, files containing graphs on which to run the algorithms. The _planarity_ executable program also supports an extensive list of command-line parameters that make it possible to automate the execution of any of the algorithms included in the application. Run _planarity_ with the "-h" command-line parameter to get more information about the command line options, and use "-h -menu" for more extensive information about command-line mode. Essentially, all functionality available in the interactive, menu-driven mode is also available via the command-line parameters. ### Setting up a Development Environment A development environment for the C reference implementations can be set up based on Eclipse. 1. Install a recent version of the Java JDK (such as Java version 14 or higher) 2. Ensure that you set the JAVA_HOME system environment variable (e.g. to c:\Program Files\Java\jdk-14.0.1) 3. Ensure that you add %JAVA_HOME%\bin to your system PATH 4. Install Eclipse, such as the "Eclipse IDE for Enterprise Java Developers" 5. Install gcc, gdb, and msys (e.g. download and run mingw-get-setup.exe from [here](https://osdn.net/projects/mingw/releases/) and then use the package installer to install C and C++, GDB, MSYS, and any other packages you may want.) 6. Ensure your gcc is accessible from the command line (e.g. add C:\MinGW\bin to the system PATH) 7. In Eclipse, and install the C Development Tools (CDT) 1. In Eclipse, choose the menu option Help > Install New Software 2. Choose to work with the main repository (e.g. 2020 - 06 - http://download.eclipse.org/releases) 3. Under Programming Languages, choose C/C++ Autotools, C/C++ Development Tools, C/C++ Development Tools SDK, C/C++ Library API Documentation Hover Help, and C/C++ Unit Testing Support ### Working with the Code in the Development Environment 1. Copy the HTTPS clone URL into the copy/paste buffer 1. In this repository, the "Code" button provides the [HTTPS clone](https://github.com/graph-algorithms/edge-addition-planarity-suite.git) link to use to get the code. 2. Start by making a new eclipse workspace for 'graph-algorithms' 1. You may need to select 'Prompt for Workspace on startup' in Window | Preferences | General | Startup and Shutdown | Workspaces, then close and reopen eclipse 2. In the initial workspace dialogue, one can specify a new folder that gets created, e.g. c:\Users\\_you_\Documents\eclipse\workspaces-cpp\graph-algorithms 3. Click 'Checkout projects from Git' in the new workspace Welcome page 4. Pick 'Clone URI' and hit Next 5. The URI, Host, and Repository are pre-filled correctly from the copy/paste clipboard. 6. Leave the User/Password blank (unless you have read/write access to the project), and hit Next 7. The master branch is selected by default, so just hit Next again 8. Change the destination directory to a subdirectory where you want to store the project code (e.g. c:\Users\\_you_\Documents\eclipse\workspaces-cpp\graph-algorithms\edge-addition-planarity-suite) 9. Hit Next (which downloads the project), Hit Next again (to Import existing Eclipse projects), Hit Finish Now that the project is available, the code can be built and executed: 1. Open the C/C++ Perspective 1. Use the Open Perspectives button (or use Windows | Perspective | Open Perspective | Other...) 2. Select C/C++ 2. Right-click Planarity-C project, Build Configurations, Build All 3. Right-click Planarity-C project, Build Configurations, Set Active, Release 4. Right-click Planarity-C project, Run As, Local Application, planarity.exe (release) ### Making the Distribution Once one has set up the development environment and is able to work with the code in the development environment, it is possible to make the distribution with the following additional steps: 1. Ensure that the autotools, configure, and make are available on the command-line (e.g. add C:\MinGW\msys\1.0\bin to the system PATH before Windows Program Files to ensure that the _find_ program is the one from MSYS rather than the one from Windows (e.g., adjust the PATH variable as needed)). 2. Navigate to .../edge-addition-planarity-suite (the directory containing _configure.ac_ and the _c_ subdirectory) 3. Get into _bash_ (e.g., type _bash_ in the Windows command-line), then enter the following commands: 1. autogen.sh 2. configure 3. make dist 4. make distcheck The result is a validated _planarity-N.N.N.N.tar.gz_ distribution, where _N.N.N.N_ is the version number expressed in the _configure.ac_ file. ### Making and Running the Software from the Distribution If you have done the steps to set up the development environment and work with the code, then you can make and run the software using the development environment, so you don't necessarily need to make or run the software using the process below. You also don't necessarily need to make and install the planarity software on Linux if you are able to get it using _sudo apt-get planarity_ . However, you may have only downloaded the distribution (i.e., _planarity-N.N.N.N.tar.gz_ ) from a Release tag of this project. Once you have decompressed the distribution into a directory, you can make it by getting into _bash_ (e.g., type _bash_ in the Windows command-line) and then entering the following commands: 1. configure 2. make At this point, the planarity executable can be run from within the distribution directory. For example, on Windows, go to the ".libs" subdirectory containing the planarity executuable and the libplanarity DLL and run _planarity -test ../c/samples_ on the command-line. On Linux, the planarity program can also be installed by entering _sudo make install_ on the command-line. Note that the _libplanarity_ shared object and symlinks will be installed to _/usr/local/lib_ so it will be necessary to set LD_LIBRARY_PATH accordingly. For one session, this can be done with _export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib_ . To make it more permanent, you could use: 1. Create a new file " _/etc/ld.so.conf.d/planarity.conf_ " containing " _/usr/local/lib_ " (without the quotes) 2. sudo ldconfig ## Contributing Subject to your acceptance of the license agreement, contributions can be made via a pull request. Before submitting a pull request, please ensure that you have set your github user name and email within your development environment. For Eclipse, you can use the following steps: 1. Window > Preferences > Team > Git > Configuration 2. Add Entry... user.name (set the value to your github identity) 3. Add Entry... user.email (set the value to the primary email of your github identity) 4. Hit Apply and Close ## Versioning The APIs for the graph library and the planarity algorithm implementations are versioned using the method documented in [configure.ac](configure.ac). The _planarity.exe_ application, which provides command-line and menu-driven interfaces for the graph library and planarity algorithms, is versioned according to the _Major.Minor.Maintenance.Tweak_ numbering system documented in the comments in [planarity.c](c/planarity.c). ## License This project is licensed under a 3-clause BSD License appearing in [LICENSE.TXT](LICENSE.TXT). ## Related Works and Further Documentation There have been successful technology transfers of the implementation code and/or algorithms of this project into other projects. To see a list of the related projects and for further documentation about this project, please see the [project wiki](https://github.com/graph-algorithms/edge-addition-planarity-suite/wiki). edge-addition-planarity-suite-Version_3.0.2.0/autogen.sh000077500000000000000000000001561420450503700232070ustar00rootroot00000000000000#!/bin/sh aclocal && autoconf && libtoolize --copy && automake --add-missing --copy && rm -rf autom4te.cache edge-addition-planarity-suite-Version_3.0.2.0/c/000077500000000000000000000000001420450503700214265ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/.cproject000066400000000000000000001076111420450503700232460ustar00rootroot00000000000000 edge-addition-planarity-suite-Version_3.0.2.0/c/.gdbinit000066400000000000000000000000241420450503700230430ustar00rootroot00000000000000set new-console on edge-addition-planarity-suite-Version_3.0.2.0/c/.project000066400000000000000000000040751420450503700231030ustar00rootroot00000000000000 Planarity-C org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.buildArguments org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.buildLocation ${workspace_loc:/Planarity-C/Debug} org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd true org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.managedbuilder.core.ScannerConfigNature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.core.cnature edge-addition-planarity-suite-Version_3.0.2.0/c/appconst.h000066400000000000000000000041351420450503700234310ustar00rootroot00000000000000#ifndef APPCONST_H #define APPCONST_H /* Copyright (c) 1997-2022, John M. Boyer All rights reserved. See the LICENSE.TXT file for licensing information. */ #ifdef WIN32 #define WINDOWS #endif /* Defines fopen strings for reading and writing text files on PC and UNIX */ #ifdef WINDOWS #define READTEXT "rt" #define WRITETEXT "wt" #define FILE_DELIMITER '\\' #else #define READTEXT "r" #define WRITETEXT "w" #define FILE_DELIMITER '/' #endif // 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 /******************************************************************** 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.2.0/c/graph.h000066400000000000000000000104071420450503700227020ustar00rootroot00000000000000#ifndef GRAPH_H #define GRAPH_H /* Copyright (c) 1997-2022, 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); int gp_ReadFromString(graphP theGraph, char *inputStr); #define WRITE_ADJLIST 1 #define WRITE_ADJMATRIX 2 #define WRITE_DEBUGINFO 3 int gp_Write(graphP theGraph, char *FileName, int Mode); int gp_WriteToString(graphP theGraph, char **pOutputStr, 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.2.0/c/graphDFSUtils.c000066400000000000000000000375231420450503700242630ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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 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"); 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.2.0/c/graphDrawPlanar.c000066400000000000000000001223541420450503700246560ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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)) { memcpy((char *) visRep + (2*Pos) * (M+1) + Mid, (char *) 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_RenderToString() Creates a rendition of the planar graph visibility representation as a string, and return it via the pRenditionString parameter. The caller can use free() to get rid of the returned string after use. Returns NOTOK for any error, OK otherwise. ********************************************************************/ int gp_DrawPlanar_RenderToString(graphP theEmbedding, char **pRenditionString) { if (theEmbedding != NULL && sp_IsEmpty(theEmbedding->edgeHoles) && pRenditionString != NULL) { *pRenditionString = _RenderToString(theEmbedding); return *pRenditionString != NULL ? OK : NOTOK; } return NOTOK; } /******************************************************************** gp_DrawPlanar_RenderToFile() Creates a rendition of the planar graph visibility representation as a string, then dumps the string to the file. theFileName - can be "stdout", "stderr" or a file system filename Returns NOTOK for any error, OK otherwise. ********************************************************************/ int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName) { if (theEmbedding != NULL && 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.2.0/c/graphDrawPlanar.h000066400000000000000000000011141420450503700246510ustar00rootroot00000000000000#ifndef GRAPH_DRAWPLANAR_H #define GRAPH_DRAWPLANAR_H /* Copyright (c) 1997-2022, 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); int gp_DrawPlanar_RenderToString(graphP theEmbedding, char **pRenditionString); #ifdef __cplusplus } #endif #endif edge-addition-planarity-suite-Version_3.0.2.0/c/graphDrawPlanar.private.h000066400000000000000000000056721420450503700263370ustar00rootroot00000000000000#ifndef GRAPH_DRAWPLANAR_PRIVATE_H #define GRAPH_DRAWPLANAR_PRIVATE_H /* Copyright (c) 1997-2022, 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.2.0/c/graphDrawPlanar_Extensions.c000066400000000000000000000607001420450503700270710ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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, "\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.2.0/c/graphEmbed.c000066400000000000000000001670131420450503700236400ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphExtensions.c000066400000000000000000000520261420450503700247600ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphExtensions.h000066400000000000000000000015451420450503700247650ustar00rootroot00000000000000#ifndef GRAPH_EXTENSIONS_H #define GRAPH_EXTENSIONS_H /* Copyright (c) 1997-2022, 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.2.0/c/graphExtensions.private.h000066400000000000000000000011221420450503700264250ustar00rootroot00000000000000#ifndef GRAPH_EXTENSIONS_PRIVATE_H #define GRAPH_EXTENSIONS_PRIVATE_H /* Copyright (c) 1997-2022, 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.2.0/c/graphFunctionTable.h000066400000000000000000000033631420450503700253630ustar00rootroot00000000000000#ifndef GRAPHFUNCTIONTABLE_H #define GRAPHFUNCTIONTABLE_H /* Copyright (c) 1997-2022, 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.2.0/c/graphIO.c000066400000000000000000000777371420450503700231500ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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, strBufP inBuf); int _ReadAdjList(graphP theGraph, FILE *Infile, strBufP inBuf); int _WriteAdjList(graphP theGraph, FILE *Outfile, strBufP outBuf); int _WriteAdjMatrix(graphP theGraph, FILE *Outfile, strBufP outBuf); 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, strBufP inBuf) { int N, v, w, Flag; if (Infile == NULL && inBuf == NULL) return NOTOK; // Read the number of vertices from the first line of the file if (Infile != NULL) fscanf(Infile, " %d ", &N); else { sb_ReadSkipWhitespace(inBuf); sscanf(sb_GetReadString(inBuf), " %d ", &N); sb_ReadSkipInteger(inBuf); sb_ReadSkipWhitespace(inBuf); } // Initialize the graph based on the number of vertices if (gp_InitGraph(theGraph, N) != OK) return NOTOK; // Read an upper-triangular matrix row for each vertex // Note that for the last vertex, zero flags are read, per the upper triangular format for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) { gp_SetVertexIndex(theGraph, v, v); for (w = v+1; gp_VertexInRange(theGraph, w); w++) { // Read each of v's w-neighbor flags if (Infile != NULL) fscanf(Infile, " %1d", &Flag); else { sb_ReadSkipWhitespace(inBuf); sscanf(sb_GetReadString(inBuf), " %1d", &Flag); sb_ReadSkipInteger(inBuf); } // Add the edge (v, w) if the flag is raised 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, strBufP inBuf) { int N, v, W, adjList, e, indexValue, ErrorCode; int zeroBased = FALSE; if (Infile == NULL && inBuf == NULL) return NOTOK; // Skip the "N=" and then read the N value for number of vertices if (Infile != NULL) { fgetc(Infile); fgetc(Infile); fscanf(Infile, " %d ", &N); } else { sb_ReadSkipChar(inBuf); sb_ReadSkipChar(inBuf); sb_ReadSkipWhitespace(inBuf); sscanf(sb_GetReadString(inBuf), " %d ", &N); sb_ReadSkipInteger(inBuf); sb_ReadSkipWhitespace(inBuf); } // Initialize theGraph based on the number of vertices in the input if (gp_InitGraph(theGraph, N) != OK) 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 if (Infile != NULL) fscanf(Infile, "%d", &indexValue); else { sscanf(sb_GetReadString(inBuf), "%d", &indexValue); sb_ReadSkipInteger(inBuf); } 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 if (Infile != NULL) fgetc(Infile); else sb_ReadSkipChar(inBuf); // 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) if (Infile != NULL) fscanf(Infile, " %d ", &W); else { sb_ReadSkipWhitespace(inBuf); sscanf(sb_GetReadString(inBuf), " %d ", &W); sb_ReadSkipInteger(inBuf); sb_ReadSkipWhitespace(inBuf); } 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, NULL); else if (Ch == 'L') RetVal = _ReadLEDAGraph(theGraph, Infile); else RetVal = _ReadAdjMatrix(theGraph, Infile, NULL); 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; } /******************************************************************** gp_ReadFromString() Populates theGraph using the information stored in inputStr. Supports adjacency list and adjacency matrix formats, not LEDA. Returns NOTOK for any error, or OK otherwise ********************************************************************/ int gp_ReadFromString(graphP theGraph, char *inputStr) { int RetVal; char Ch; strBufP inBuf = sb_New(0); if (inBuf == NULL) return NOTOK; if (sb_ConcatString(inBuf, inputStr) != OK) { sb_Free(&inBuf); return NOTOK; } Ch = sb_GetReadString(inBuf)[0]; if (Ch == 'N') RetVal = _ReadAdjList(theGraph, NULL, inBuf); else if (Ch == 'L') { sb_Free(&inBuf); return NOTOK; } else RetVal = _ReadAdjMatrix(theGraph, NULL, inBuf); if (RetVal == OK) { char *extraData = sb_GetReadString(inBuf); int extraDataLen = extraData == NULL ? 0 : strlen(extraData); if (extraDataLen > 0) RetVal = theGraph->functions.fpReadPostprocess(theGraph, extraData, extraDataLen); } sb_Free(&inBuf); 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 for parameter errors; OK otherwise. ********************************************************************/ int _WriteAdjList(graphP theGraph, FILE *Outfile, strBufP outBuf) { int v, e; int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; char numberStr[128]; if (theGraph==NULL || (Outfile==NULL && outBuf == NULL)) return NOTOK; // Write the number of vertices of the graph to the file or string buffer if (Outfile != NULL) fprintf(Outfile, "N=%d\n", theGraph->N); else { sprintf(numberStr, "N=%d\n", theGraph->N); if (sb_ConcatString(outBuf, numberStr) != OK) return NOTOK; } // Write the adjacency list of each vertex for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) { if (Outfile != NULL) fprintf(Outfile, "%d:", v - zeroBasedOffset); else { sprintf(numberStr, "%d:", v - zeroBasedOffset); if (sb_ConcatString(outBuf, numberStr) != OK) return NOTOK; } e = gp_GetLastArc(theGraph, v); while (gp_IsArc(e)) { if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) { if (Outfile != NULL) fprintf(Outfile, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset); else { sprintf(numberStr, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset); if (sb_ConcatString(outBuf, numberStr) != OK) return NOTOK; } } e = gp_GetPrevArc(theGraph, e); } // Write NIL at the end of the adjacency list (in zero-based I/O, NIL was -1) if (Outfile != NULL) fprintf(Outfile, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL); else { sprintf(numberStr, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL); if (sb_ConcatString(outBuf, numberStr) != OK) return NOTOK; } } return OK; } /******************************************************************** _WriteAdjMatrix() Outputs upper triangular matrix representation capable of being read by _ReadAdjMatrix(). theGraph and one of Outfile or theStrBuf must be non-NULL. 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, strBufP outBuf) { int v, e, K; char *Row = NULL; char numberStr[128]; if (theGraph == NULL || (Outfile == NULL && outBuf == NULL)) return NOTOK; // Write the number of vertices in the graph to the file or string buffer if (Outfile != NULL) fprintf(Outfile, "%d\n", theGraph->N); else { sprintf(numberStr, "%d\n", theGraph->N); if (sb_ConcatString(outBuf, numberStr) != OK) return NOTOK; } // Allocate memory for storing a string expression of one row at a time Row = (char *) malloc((theGraph->N+2)*sizeof(char)); if (Row == NULL) return NOTOK; // Construct the upper triangular matrix representation one row at a time 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, K); 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] = '\n'; Row[theGraph->N+1] = '\0'; // Write the row to the file or string buffer if (Outfile != NULL) fprintf(Outfile, "%s", Row); else sb_ConcatString(outBuf, 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, NULL); break; case WRITE_ADJMATRIX : RetVal = _WriteAdjMatrix(theGraph, Outfile, NULL); 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; } /******************************************************************** * gp_WriteToString() * * Writes the information of theGraph into a string that is returned * to the caller via the pointer pointer pOutputStr. * The string is owned by the caller and should be released with * free() when the caller doesn't need the string anymore. * The format of the content written into the returned string is based * on the Mode parameter: WRITE_ADJLIST or WRITE_ADJMATRIX * (the WRITE_DEBUGINFO Mode is not supported at this time) NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST Returns NOTOK on error, or OK on success along with an allocated string *pOutputStr that the caller must free() ********************************************************************/ int gp_WriteToString(graphP theGraph, char **pOutputStr, int Mode) { int RetVal; strBufP outBuf = sb_New(0); if (theGraph == NULL || pOutputStr == NULL || outBuf == NULL) { sb_Free(&outBuf); return NOTOK; } switch (Mode) { case WRITE_ADJLIST : RetVal = _WriteAdjList(theGraph, NULL, outBuf); break; case WRITE_ADJMATRIX : RetVal = _WriteAdjMatrix(theGraph, NULL, outBuf); break; default : RetVal = NOTOK; break; } if (RetVal == OK) { void *extraData = NULL; long extraDataSize; RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData, &extraDataSize); if (extraData != NULL) { for (int i = 0; i < extraDataSize; i++) sb_ConcatChar(outBuf, ((char *) extraData)[i]); free(extraData); } } *pOutputStr = sb_TakeString(outBuf); sb_Free(&outBuf); 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.2.0/c/graphIsolator.c000066400000000000000000000731401420450503700244150ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphK23Search.c000066400000000000000000000225111420450503700243020ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphK23Search.h000066400000000000000000000006501420450503700243070ustar00rootroot00000000000000#ifndef GRAPH_K23SEARCH_H #define GRAPH_K23SEARCH_H /* Copyright (c) 1997-2022, 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.2.0/c/graphK23Search.private.h000066400000000000000000000006461420450503700257650ustar00rootroot00000000000000#ifndef GRAPH_K23SEARCH_PRIVATE_H #define GRAPH_K23SEARCH_PRIVATE_H /* Copyright (c) 1997-2022, 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.2.0/c/graphK23Search_Extensions.c000066400000000000000000000210501420450503700265160ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphK33Search.c000066400000000000000000002406261420450503700243140ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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 Z; /* 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_Discard(tempStack); /* Move (R, Rout) out of the way */ sp_Pop2_Discard1(tempStack, Z); /* Get Z, discard 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; 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)) { 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, 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); // int 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.2.0/c/graphK33Search.h000066400000000000000000000006501420450503700243100ustar00rootroot00000000000000#ifndef GRAPH_K33SEARCH_H #define GRAPH_K33SEARCH_H /* Copyright (c) 1997-2022, 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.2.0/c/graphK33Search.private.h000066400000000000000000000025131420450503700257610ustar00rootroot00000000000000#ifndef GRAPH_K33SEARCH_PRIVATE_H #define GRAPH_K33SEARCH_PRIVATE_H /* Copyright (c) 1997-2022, 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.2.0/c/graphK33Search_Extensions.c000066400000000000000000000656071420450503700265370ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphK4Search.c000066400000000000000000001615101420450503700242240ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphK4Search.h000066400000000000000000000006421420450503700242270ustar00rootroot00000000000000#ifndef GRAPH_K4SEARCH_H #define GRAPH_K4SEARCH_H /* Copyright (c) 1997-2022, 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.2.0/c/graphK4Search.private.h000066400000000000000000000027611420450503700257040ustar00rootroot00000000000000#ifndef GRAPH_K4SEARCH_PRIVATE_H #define GRAPH_K4SEARCH_PRIVATE_H /* Copyright (c) 1997-2022, 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.2.0/c/graphK4Search_Extensions.c000066400000000000000000000423211420450503700264410ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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. // The top of stack has the pair R and 0/1 direction Walkdown traversal proceeds from R // Need only R, so pop and discard the direction, then pop R sp_Pop2_Discard1(theGraph->theStack, R); // 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.2.0/c/graphNonplanar.c000066400000000000000000000701001420450503700245420ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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 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; 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)) { // The top of stack has the pair R and 0/1 direction Walkdown traversal proceeds from R // Need only R, so pop and discard the direction, then pop R sp_Pop2_Discard1(theGraph->theStack, R); } 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 R, X, Y, W, Z, ZPrevLink, ZType; // Unpack the context for efficiency of loops 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, W; int stackBottom1, stackBottom2; /* Initialization */ R = theGraph->IC.r; 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.2.0/c/graphOuterplanarObstruction.c000066400000000000000000000160151420450503700273470ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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 W; // Create the initial non-outerplanarity obstruction isolator state. if (_InitializeNonplanarityContext(theGraph, v, R) != OK) return NOTOK; R = theGraph->IC.r; 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.2.0/c/graphStructures.h000066400000000000000000001071521420450503700250120ustar00rootroot00000000000000#ifndef GRAPHSTRUCTURE_H #define GRAPHSTRUCTURE_H /* Copyright (c) 1997-2022, 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 "strbuf.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.2.0/c/graphTests.c000066400000000000000000001075031420450503700237240ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/graphUtils.c000066400000000000000000002563461420450503700237340ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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 = gp_GetNeighborEdgeRecord(theGraph, u, v); 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 = gp_GetFirstVertex(theGraph); 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.2.0/c/listcoll.c000066400000000000000000000224621420450503700234250ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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.2.0/c/listcoll.h000066400000000000000000000107631420450503700234330ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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); If theList is empty, then theNode becomes its only member and is returned. Otherwise, theNode is placed before theList head, and theNode is returned as the new head. */ #define LCPrepend(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, \ listColl->List[theList].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, and then theList head 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.2.0/c/planarity.c000066400000000000000000000217761420450503700236120ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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, change of backward compatibility) // Minor is for feature addition (e.g. a new algorithm implementation added, new interface) // Maintenance is for functional revision (e.g. bug fix to existing algorithm implementation) // Tweak is for a non-functional revision (e.g. change of build scripts or testing code, user-facing string changes) // 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.2.0" "\nCopyright (c) 1997-2022 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" "\n"; ProjectTitle(); if (param == NULL) { Message( "'planarity': if no command-line, then menu-driven\n" "'planarity (-h|-help)': this message\n" "'planarity (-h|-help) -menu': more help with menu-based command line\n" "'planarity (-i|-info): copyright and license information\n" "'planarity -test [-q] [samples dir]': runs tests (optional quiet mode)\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 version 3.0.2.0\n" "Copyright (c) 1997-2022, 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://dx.doi.org/10.7155/jgaa.00268\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" " http://dx.doi.org/10.1007/11618058_47\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. http://dx.doi.org/10.7155/jgaa.00091\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. https://dspace.library.uvic.ca/handle/1828/9918\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]': Random maximal planar graph\n" "'planarity -rn [-q] N O [O2]': Random nonplanar 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 -rm and -rn, 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 OK; } /**************************************************************************** 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 == 'd' || Choice == 'o') secondOutfile =""; if (!strchr("pdo234", Choice)) { Message("Invalid menu choice, please try again."); } else { switch (tolower(Mode)) { case 's' : SpecificGraph(Choice, NULL, NULL, secondOutfile, NULL, NULL, NULL); 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.2.0/c/planarity.h000066400000000000000000000040641420450503700236060ustar00rootroot00000000000000#ifndef PLANARITY_H #define PLANARITY_H /* Copyright (c) 1997-2022, 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, char *inputStr, char **pOutputStr, char **pOutput2Str ); int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name); int RandomGraphs(char command, int, int); /* Command line, Menu, and Configuration */ int commandLine(int argc, char *argv[]); int legacyCommandLine(int argc, char *argv[]); int menu(); extern char Mode, OrigOut, EmbeddableOut, ObstructedOut, AdjListsForEmbeddingsOut, quietMode; void Reconfigure(); /* Low-level Utilities */ #define MAXLINE 1024 extern 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); char *ReadTextFileIntoString(char *infileName); int TextFileMatchesString(char *theFilename, char *theString); int TextFilesEqual(char *file1Name, char *file2Name); int BinaryFilesEqual(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.2.0/c/planarityCommandLine.c000066400000000000000000000311111420450503700257010ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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; } /**************************************************************************** Quick regression test ****************************************************************************/ int runSpecificGraphTests(char *); int runSpecificGraphTest(char *command, char *infileName, int inputInMemFlag); int runQuickRegressionTests(int argc, char *argv[]) { char *samplesDir = "samples"; int samplesDirArgLocation = 2; // Skip optional -q quiet mode command-line paramater, if present if (argc > samplesDirArgLocation && strcmp(argv[samplesDirArgLocation], "-q") == 0) samplesDirArgLocation++; // Accept overriding sample directory command-line parameter, if present if (argc > samplesDirArgLocation) samplesDir = argv[samplesDirArgLocation]; if (runSpecificGraphTests(samplesDir) < 0) return NOTOK; return OK; } int runSpecificGraphTests(char *samplesDir) { char origDir[2049]; int retVal = 0; if (!getcwd(origDir, 2048)) return -1; // Preserve original behavior before the samplesDir command-line parameter was available if (strcmp(samplesDir, "samples") == 0) { if (chdir(samplesDir) != 0) { if (chdir("..") != 0 || chdir(samplesDir) != 0) { // Give success result, but Warn if no samples (except no warning if in quiet mode) Message("WARNING: Unable to change to samples directory to run tests on samples.\n"); return 0; } } } else { // New behavior if samplesDir command-line parameter was specified if (chdir(samplesDir) != 0) { Message("WARNING: Unable to change to samples directory to run tests on samples.\n"); return 0; } } #if NIL == 0 Message("Starting NIL == 0 Tests\n"); if (runSpecificGraphTest("-p", "maxPlanar5.txt", TRUE) < 0) { retVal = -1; Message("Planarity test on maxPlanar5.txt failed.\n"); } if (runSpecificGraphTest("-d", "maxPlanar5.txt", FALSE) < 0) { retVal = -1; Message("Graph drawing test maxPlanar5.txt failed.\n"); } if (runSpecificGraphTest("-d", "drawExample.txt", TRUE) < 0) { retVal = -1; Message("Graph drawing on drawExample.txt failed.\n"); } if (runSpecificGraphTest("-p", "Petersen.txt", FALSE) < 0) { retVal = -1; Message("Planarity test on Petersen.txt failed.\n"); } if (runSpecificGraphTest("-o", "Petersen.txt", TRUE) < 0) { retVal = -1; Message("Outerplanarity test on Petersen.txt failed.\n"); } if (runSpecificGraphTest("-2", "Petersen.txt", FALSE) < 0) { retVal = -1; Message("K_{2,3} search on Petersen.txt failed.\n"); } if (runSpecificGraphTest("-3", "Petersen.txt", TRUE) < 0) { retVal = -1; Message("K_{3,3} search on Petersen.txt failed.\n"); } if (runSpecificGraphTest("-4", "Petersen.txt", FALSE) < 0) { retVal = -1; Message("K_4 search on Petersen.txt failed.\n"); } Message("Finished NIL == 0 Tests.\n\n"); #endif if (runSpecificGraphTest("-p", "maxPlanar5.0-based.txt", FALSE) < 0) { retVal = -1; Message("Planarity test on maxPlanar5.0-based.txt failed.\n"); } if (runSpecificGraphTest("-d", "maxPlanar5.0-based.txt", TRUE) < 0) { retVal = -1; Message("Graph drawing test maxPlanar5.0-based.txt failed.\n"); } if (runSpecificGraphTest("-d", "drawExample.0-based.txt", FALSE) < 0) { retVal = -1; Message("Graph drawing on drawExample.0-based.txt failed.\n"); } if (runSpecificGraphTest("-p", "Petersen.0-based.txt", TRUE) < 0) { retVal = -1; Message("Planarity test on Petersen.0-based.txt failed.\n"); } if (runSpecificGraphTest("-o", "Petersen.0-based.txt", FALSE) < 0) { retVal = -1; Message("Outerplanarity test on Petersen.0-based.txt failed.\n"); } if (runSpecificGraphTest("-2", "Petersen.0-based.txt", TRUE) < 0) { retVal = -1; Message("K_{2,3} search on Petersen.0-based.txt failed.\n"); } if (runSpecificGraphTest("-3", "Petersen.0-based.txt", FALSE) < 0) { retVal = -1; Message("K_{3,3} search on Petersen.0-based.txt failed.\n"); } if (runSpecificGraphTest("-4", "Petersen.0-based.txt", TRUE) < 0) { retVal = -1; Message("K_4 search on Petersen.0-based.txt failed.\n"); } if (retVal == 0) Message("Tests of all specific graphs succeeded.\n"); else Message("One or more specific graph tests FAILED.\n"); chdir(origDir); FlushConsole(stdout); return retVal; } int runSpecificGraphTest(char *command, char *infileName, int inputInMemFlag) { int Result = OK; char algorithmCode = command[1]; // The algorithm, indicated by algorithmCode, operating on 'infilename' is expected to produce // an output that is stored in the file named 'expectedResultFileName' (return string not owned) char *expectedPrimaryResultFileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]); char *inputString = NULL; char *actualOutput = NULL; char *actualOutput2 = NULL; // SpecificGraph() can invoke gp_Read() if the graph is to be read from a file, or it can invoke // gp_ReadFromString() if the inputInMemFlag is set. if (inputInMemFlag) { inputString = ReadTextFileIntoString(infileName); if (inputString == NULL) { ErrorMessage("Failed to read input file into string.\n"); Result = NOTOK; } } if (Result == OK) { // Perform the indicated algorithm on the graph in the input file or string. Result = SpecificGraph(algorithmCode, infileName, NULL, NULL, inputString, &actualOutput, &actualOutput2); } // Change from internal OK/NONEMBEDDABLE/NOTOK result to a command-line style 0/-1 result if (Result == OK || Result == NONEMBEDDABLE) Result = 0; else { ErrorMessage("Test failed (graph processor returned failure result).\n"); Result = -1; } // Test that the primary actual output matches the primary expected output if (Result == 0) { if (TextFileMatchesString(expectedPrimaryResultFileName, actualOutput) == TRUE) Message("Test succeeded (result equal to exemplar).\n"); else { ErrorMessage("Test failed (result not equal to exemplar).\n"); Result = -1; } } // Test that the secondary actual output matches the secondary expected output if (algorithmCode == 'd' && Result == 0) { char *expectedSecondaryResultFileName = (char *) malloc(strlen(expectedPrimaryResultFileName) + strlen(".render.txt") + 1); if (expectedSecondaryResultFileName != NULL) { sprintf(expectedSecondaryResultFileName, "%s%s", expectedPrimaryResultFileName, ".render.txt"); if (TextFileMatchesString(expectedSecondaryResultFileName, actualOutput2) == TRUE) Message("Test succeeded (secondary result equal to exemplar).\n"); else { ErrorMessage("Test failed (secondary result not equal to exemplar).\n"); Result = -1; } free(expectedSecondaryResultFileName); } else { Result = -1; } } // Cleanup and then return the command-line style result code Message("\n"); if (inputString != NULL) free(inputString); if (actualOutput != NULL) free(actualOutput); if (actualOutput2 != NULL) free(actualOutput2); 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, NULL, NULL, NULL); } /**************************************************************************** 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.2.0/c/planarityRandomGraphs.c000066400000000000000000000416251420450503700261130ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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; int writeResult; int writeErrorReported_Random=FALSE, writeErrorReported_Embedded=FALSE, writeErrorReported_AdjList=FALSE, writeErrorReported_Obstructed=FALSE, writeErrorReported_Error=FALSE; 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%c%d.txt", FILE_DELIMITER, K%10); writeResult = gp_Write(theGraph, theFileName, WRITE_ADJLIST); if (writeResult != OK && !writeErrorReported_Random) { sprintf(Line, "Failed to write graph %s\nMake the directory if not present\n", theFileName); ErrorMessage(Line); writeErrorReported_Random = TRUE; } } 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%c%d.txt", FILE_DELIMITER, K%10); writeResult = gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); if (writeResult != OK && !writeErrorReported_Embedded) { sprintf(Line, "Failed to write graph %s\nMake the directory if not present\n", theFileName); ErrorMessage(Line); writeErrorReported_Embedded = TRUE; } } if (tolower(AdjListsForEmbeddingsOut) == 'y') { sprintf(theFileName, "adjlist%c%d.txt", FILE_DELIMITER, K%10); writeResult = gp_Write(theGraph, theFileName, WRITE_ADJLIST); if (writeResult != OK && !writeErrorReported_AdjList) { sprintf(Line, "Failed to write graph %s\nMake the directory if not present\n", theFileName); ErrorMessage(Line); writeErrorReported_AdjList = TRUE; } } } 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%c%d.txt", FILE_DELIMITER, K%10); writeResult = gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); if (writeResult != OK && !writeErrorReported_Obstructed) { sprintf(Line, "Failed to write graph %s\nMake the directory if not present\n", theFileName); ErrorMessage(Line); writeErrorReported_Obstructed = TRUE; } } } } } // If there is an error in processing, then write the file for debugging if (Result != OK && Result != NONEMBEDDABLE) { sprintf(theFileName, "error%c%d.txt", FILE_DELIMITER, K%10); writeResult = gp_Write(origGraph, theFileName, WRITE_ADJLIST); if (writeResult != OK && !writeErrorReported_Error) { sprintf(Line, "Failed to write graph %s\nMake the directory if not present\n", theFileName); ErrorMessage(Line); writeErrorReported_Error = TRUE; } } } // 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); if (quietMode == 'n') { Prompt("Do you want to save the generated graph in edge list format (y/n)? "); fflush(stdin); scanf(" %c", &saveEdgeListFormat); } else saveEdgeListFormat = 'n'; if (tolower(saveEdgeListFormat) == 'y') { char theFileName[256]; if (extraEdges > 0) strcpy(theFileName, "nonPlanarEdgeList.txt"); else strcpy(theFileName, "maxPlanarEdgeList.txt"); sprintf(Line, "Saving edge list format of original graph to '%s'\n", theFileName); Message(Line); SaveAsciiGraph(origGraph, theFileName); strcat(theFileName, ".out.txt"); sprintf(Line, "Saving edge list format of result to '%s'\n", theFileName); Message(Line); SaveAsciiGraph(theGraph, theFileName); } } else ErrorMessage("Failure occurred"); gp_Free(&theGraph); gp_Free(&origGraph); FlushConsole(stdout); return Result; } edge-addition-planarity-suite-Version_3.0.2.0/c/planaritySpecificGraph.c000066400000000000000000000216751420450503700262400ustar00rootroot00000000000000/* Copyright (c) 1997-2022, John M. Boyer All rights reserved. See the LICENSE.TXT file for licensing information. */ #include "planarity.h" /**************************************************************************** SpecificGraph() command - a menu letter (e.g. p,d,o,2,3,4) indicating the algorithm to run on the specific graph infilename - name of file to read, or NULL to cause the program to prompt the user for a filename outfilename - name of primary output file, or NULL to construct an output filename based on the input outfile2Name - name of a secondary output file, or NULL to suppress secondary output, or empty string to construct the secondary output filename based on the output filename. For p=planarity and o=outerplanarity, empty string means that the planarity or outerplanarity obstruction will be written to outfilename, rather than only an embedding For d=drawing a planar graph, empty string means the visibility representation will be written to outfilename+".render.txt" inputStr - if non-NULL, overrides infilename and provides the input graph within a string pOutputStr - if non-NULL, overrides outfilename and provides a pointer pointer where a string containing the primary output should go. For p=planarity, o=outerplanarity, and d=drawing, the primary output is the graph embedding For p=planarity and o=outerplanarity, if the graph is not embeddable, then the primary output will contain the planarity or outerplanarity obstruction subgraph For 2,3,4=subgraph homeomorphism, the primary output is the homeomorphic subgraph, if found pOutput2Str - if non-NULL, overrides outfile2Name and provides a pointer pointer where a string containing the secondary output should go. For d=drawing a planar graph, the visibility representation will be written to this secondary output ****************************************************************************/ int SpecificGraph( char command, char *infileName, char *outfileName, char *outfile2Name, char *inputStr, char **pOutputStr, char **pOutput2Str ) { graphP theGraph, origGraph; platform_time start, end; int Result = OK; // Get the filename of the graph to test if (inputStr == NULL) { 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 if (inputStr == NULL) { Result = gp_Read(theGraph, infileName); } else { Result = gp_ReadFromString(theGraph, inputStr); } 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 { int writeResult = OK; if (pOutputStr == NULL) writeResult = gp_Write(theGraph, outfileName, WRITE_ADJLIST); else writeResult = gp_WriteToString(theGraph, pOutputStr, WRITE_ADJLIST); if (writeResult != OK) Result = NOTOK; } // NOW WE WANT TO WRITE THE SECONDARY OUTPUT to a FILE or STRING // 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. if (outfile2Name != NULL || pOutput2Str != NULL) { int writeResult = OK; if (pOutput2Str != NULL) { // A non-embeddable obstruction subgraph also goes into the primary output, not the secondary if ((command == 'p' || command == 'o') && Result == NONEMBEDDABLE) writeResult = gp_WriteToString(theGraph, pOutputStr, WRITE_ADJLIST); // Only the planar visibility representation goes into the secondary output else if (command == 'd' && Result == OK) writeResult = gp_DrawPlanar_RenderToString(theGraph, pOutput2Str); } else 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; writeResult = gp_Write(theGraph, outfile2Name, WRITE_ADJLIST); } else if (command == 'd' && Result == OK) { // An empty but non-NULL string is passed to indicate the necessity // of selecting a default name for the second output file. // By default, add ".render.txt" to the primary output filename if (strlen(outfile2Name) == 0) strcat((outfile2Name = outfileName), ".render.txt"); writeResult = gp_DrawPlanar_RenderToFile(theGraph, outfile2Name); } } if (writeResult != OK) Result = NOTOK; } } // 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.2.0/c/planarityUtils.c000066400000000000000000000346661420450503700246350ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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); fflush(stdout); } } void ErrorMessage(char *message) { if (quietMode == 'n') { fprintf(stderr, "%s", message); fflush(stderr); } } void FlushConsole(FILE *f) { fflush(f); } void Prompt(char *message) { Message(message); FlushConsole(stdout); } /**************************************************************************** ****************************************************************************/ void SaveAsciiGraph(graphP theGraph, char *filename) { int e, EsizeOccupied, vertexLabelFix; FILE *outfile = fopen(filename, WRITETEXT); // The filename may specify a directory that doesn't exist if (outfile == NULL) { sprintf(Line, "Failed to write to %s\nMake the directory if not present\n", filename); ErrorMessage(Line); return; } // If filename includes path elements, remove them before writing the file's name to the file if (strrchr(filename, FILE_DELIMITER)) filename = strrchr(filename, FILE_DELIMITER)+1; fprintf(outfile, "%s\n", filename); // This edge list file format uses 1-based vertex numbering, and the current code // internally uses 1-based indexing by default, so this vertex label 'fix' adds zero // But earlier code used 0-based indexing and added one on output, so we replicate // that behavior in case the current code has been compiled with zero-based indexing. vertexLabelFix = 1 - gp_GetFirstVertex(theGraph); // Iterate over the edges of the graph EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2) { // Only output edges that haven't been deleted (i.e. skip the edge holes) if (gp_EdgeInUse(theGraph, e)) { fprintf(outfile, "%d %d\n", gp_GetNeighbor(theGraph, e) + vertexLabelFix, gp_GetNeighbor(theGraph, e+1) + vertexLabelFix); } } // Since vertex numbers are at least 1, this indicates the end of the edge list fprintf(outfile, "0 0\n"); fclose(outfile); } /**************************************************************************** ReadTextFileIntoString() Reads the file content indicated by infileName using a single fread(), and returns the result in an allocated string. The caller needs to free() the returned string when done with it. Returns NULL on error, or an allocated string containing the file content. ****************************************************************************/ char *ReadTextFileIntoString(char *infileName) { FILE *infile = NULL; char *inputString = NULL; if ((infile = fopen(infileName, "r")) == NULL) ErrorMessage("fopen() failed.\n"); else { long filePos = ftell(infile); long fileSize; fseek(infile, 0, SEEK_END); fileSize = ftell(infile); fseek(infile, filePos, SEEK_SET); if ((inputString = (char *) malloc((fileSize + 1) * sizeof(char))) != NULL) { long bytesRead = fread((void *) inputString, 1, fileSize, infile); inputString[bytesRead] = '\0'; } fclose(infile); } return inputString; } /**************************************************************************** * TextFileMatchesString() * * Compares the text file content from the file named 'theFilename' with * the content of 'theString'. * * Textual equality is measured as content equality except for suppressing * differences between CRLF and LF-only line delimiters. * * Returns TRUE if the contents are textually equal, FALSE otherwise ****************************************************************************/ int TextFileMatchesString(char *theFilename, char *theString) { FILE *infile = NULL; int Result = TRUE; if (theFilename != NULL) infile = fopen(theFilename, "r"); if (infile == NULL) Result = FALSE; else { int c1=0, c2=0, stringIndex=0; // Read the input file to the end while ((c1 = fgetc(infile)) != EOF) { // Want to suppress distinction between lines ending with CRLF versus LF // by looking at only LF characters in the file if (c1 == '\r') continue; // Since c1 now has a non-CR, non-EOF from the input file, we now also // get a character from the string, except ignoring CRs again while ((c2 = (int) theString[stringIndex++]) == '\r') ; // If c1 doesn't equal c2 (whether c2 is a null terminator or a different character) // then the file content doesn't match the string if (c1 != c2) { Result = FALSE; break; } } // If the outer while loop got to the end of the file if (c1 == EOF) { // Then get another character from the string, once again suppressing CRs, and then... while ((c2 = (int) theString[stringIndex++]) == '\r') ; // Test whether or not the second file also ends, same as the first. if (c2 != '\0') Result = FALSE; } } if (infile != NULL) fclose(infile); return Result; } /**************************************************************************** ****************************************************************************/ 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.2.0/c/platformTime.h000066400000000000000000000030121420450503700242360ustar00rootroot00000000000000#ifndef PLATFORM_TIME #define PLATFORM_TIME /* Copyright (c) 1997-2022, 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.2.0/c/samples/000077500000000000000000000000001420450503700230725ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/samples/Makefile.am000066400000000000000000000025241420450503700251310ustar00rootroot00000000000000samplesdir = @docdir@/samples dist_samples_DATA = \ Petersen.0-based.txt \ Petersen.0-based.txt.ColorVertices.out.txt \ Petersen.0-based.txt.K23Search.out.txt \ Petersen.0-based.txt.K33Search.out.txt \ Petersen.0-based.txt.K4Search.out.txt \ Petersen.0-based.txt.OuterplanarEmbed.out.txt \ Petersen.0-based.txt.PlanarEmbed.out.txt \ Petersen.txt \ Petersen.txt.ColorVertices.out.txt \ Petersen.txt.K23Search.out.txt \ Petersen.txt.K33Search.out.txt \ Petersen.txt.K4Search.out.txt \ Petersen.txt.OuterplanarEmbed.out.txt \ Petersen.txt.PlanarEmbed.out.txt \ drawExample.0-based.txt \ drawExample.0-based.txt.ColorVertices.out.txt \ drawExample.0-based.txt.DrawPlanar.out.txt \ drawExample.0-based.txt.DrawPlanar.out.txt.render.txt \ drawExample.txt \ drawExample.txt.ColorVertices.out.txt \ drawExample.txt.DrawPlanar.out.txt \ drawExample.txt.DrawPlanar.out.txt.render.txt \ maxPlanar5.0-based.txt \ maxPlanar5.0-based.txt.ColorVertices.out.txt \ maxPlanar5.0-based.txt.DrawPlanar.out.txt \ maxPlanar5.0-based.txt.DrawPlanar.out.txt.render.txt \ maxPlanar5.0-based.txt.PlanarEmbed.out.txt \ maxPlanar5.txt \ maxPlanar5.txt.ColorVertices.out.txt \ maxPlanar5.txt.DrawPlanar.out.txt \ maxPlanar5.txt.DrawPlanar.out.txt.render.txt \ maxPlanar5.txt.PlanarEmbed.out.txtedge-addition-planarity-suite-Version_3.0.2.0/c/samples/Petersen.0-based.txt000066400000000000000000000002101420450503700266230ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.0-based.txt.ColorVertices.out.txt000066400000000000000000000003471420450503700330440ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.0-based.txt.K23Search.out.txt000066400000000000000000000001541420450503700317420ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.0-based.txt.K33Search.out.txt000066400000000000000000000001741420450503700317450ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.0-based.txt.K4Search.out.txt000066400000000000000000000001641420450503700316620ustar00rootroot00000000000000N=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.txt000066400000000000000000000001541420450503700334270ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/samplesN=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.2.0/c/samples/Petersen.0-based.txt.PlanarEmbed.out.txt000066400000000000000000000001741420450503700324310ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt000066400000000000000000000002021420450503700254120ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt.ColorVertices.out.txt000066400000000000000000000003421420450503700316250ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt.K23Search.out.txt000066400000000000000000000001461420450503700305310ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt.K33Search.out.txt000066400000000000000000000001661420450503700305340ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt.K4Search.out.txt000066400000000000000000000001561420450503700304510ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt.OuterplanarEmbed.out.txt000066400000000000000000000001461420450503700322750ustar00rootroot00000000000000N=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.2.0/c/samples/Petersen.txt.PlanarEmbed.out.txt000066400000000000000000000001661420450503700312200ustar00rootroot00000000000000N=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.2.0/c/samples/drawExample.0-based.txt000066400000000000000000000003321420450503700273140ustar00rootroot00000000000000N=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.txt000066400000000000000000000005341420450503700334470ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/samplesN=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.2.0/c/samples/drawExample.0-based.txt.DrawPlanar.out.txt000066400000000000000000000016701420450503700330000ustar00rootroot00000000000000N=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.txt000066400000000000000000000013201420450503700350050ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/samples----------0----------- | | | | -3-- | | | | | | | --4--- | | | | | | | | | | | 6- | | | | | | | | | | | -13 | | | | | || | | | | --12-| | | | | | | | | | -10- | | | | | | | | | | 8-| | | | || | --7-- || | | || -----14---- || | || 9- || | || --5--- || | | || -2- | || || | || |11| || | || || --------1--------- edge-addition-planarity-suite-Version_3.0.2.0/c/samples/drawExample.txt000066400000000000000000000003161420450503700261040ustar00rootroot00000000000000N=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.2.0/c/samples/drawExample.txt.ColorVertices.out.txt000066400000000000000000000005211420450503700323100ustar00rootroot00000000000000N=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.2.0/c/samples/drawExample.txt.DrawPlanar.out.txt000066400000000000000000000016571420450503700315730ustar00rootroot00000000000000N=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.txt000066400000000000000000000013201420450503700335730ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/samples----------1----------- | | | | -4-- | | | | | | | --5--- | | | | | | | | | | | 7- | | | | | | | | | | | -14 | | | | | || | | | | --13-| | | | | | | | | | -11- | | | | | | | | | | 9-| | | | || | --8-- || | | || -----15---- || | || 10 || | || --6--- || | | || -3- | || || | || |12| || | || || --------2--------- edge-addition-planarity-suite-Version_3.0.2.0/c/samples/maxPlanar5.0-based.txt000066400000000000000000000001141420450503700270510ustar00rootroot00000000000000N=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.2.0/c/samples/maxPlanar5.0-based.txt.ColorVertices.out.txt000066400000000000000000000002151420450503700332610ustar00rootroot00000000000000N=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.2.0/c/samples/maxPlanar5.0-based.txt.DrawPlanar.out.txt000066400000000000000000000005271420450503700325370ustar00rootroot00000000000000N=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.txt000066400000000000000000000001561420450503700345520ustar00rootroot00000000000000edge-addition-planarity-suite-Version_3.0.2.0/c/samples----0---- | | | | | | -4--| | | |||| | -3--||| | | ||| ---1---|| | || ---2---- edge-addition-planarity-suite-Version_3.0.2.0/c/samples/maxPlanar5.0-based.txt.PlanarEmbed.out.txt000066400000000000000000000001141420450503700326460ustar00rootroot00000000000000N=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.2.0/c/samples/maxPlanar5.txt000066400000000000000000000001071420450503700256410ustar00rootroot00000000000000N=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.2.0/c/samples/maxPlanar5.txt.ColorVertices.out.txt000066400000000000000000000002101420450503700320420ustar00rootroot00000000000000N=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.2.0/c/samples/maxPlanar5.txt.DrawPlanar.out.txt000066400000000000000000000005241420450503700313220ustar00rootroot00000000000000N=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.2.0/c/samples/maxPlanar5.txt.DrawPlanar.out.txt.render.txt000066400000000000000000000001561420450503700334170ustar00rootroot00000000000000----1---- | | | | | | -5--| | | |||| | -4--||| | | ||| ---2---|| | || ---3---- edge-addition-planarity-suite-Version_3.0.2.0/c/samples/maxPlanar5.txt.PlanarEmbed.out.txt000066400000000000000000000001071420450503700314360ustar00rootroot00000000000000N=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.2.0/c/stack.c000066400000000000000000000107021420450503700226770ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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__Pop_Discard(stackP theStack) { if (theStack->size <= 0) return NOTOK; --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__Pop2_Discard1(stackP theStack, int *pA) { if (theStack->size <= 1) return NOTOK; // When a pair of the form (main, secondary) are pushed in order, // it is sometimes necessary to pop the secondary and discard, // then pop and store the main datum. --theStack->size; *pA = theStack->S[--theStack->size]; return OK; } int sp__Pop2_Discard(stackP theStack) { if (theStack->size <= 1) return NOTOK; --theStack->size; --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.2.0/c/stack.h000066400000000000000000000055011420450503700227050ustar00rootroot00000000000000/* Copyright (c) 1997-2022, 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_Pop_Discard(theStack) { if (sp__Pop_Discard(theStack) != OK) return NOTOK; } #define sp_Pop2(theStack, a, b) { if (sp__Pop2(theStack, &(a), &(b)) != OK) return NOTOK; } #define sp_Pop2_Discard1(theStack, a) { if (sp__Pop2_Discard1(theStack, &(a)) != OK) return NOTOK; } #define sp_Pop2_Discard(theStack) { if (sp__Pop2_Discard(theStack) != OK) return NOTOK; } int sp__Pop(stackP, int *); int sp__Pop_Discard(stackP theStack); int sp__Pop2(stackP, int *, int *); int sp__Pop2_Discard1(stackP theStack, int *pA); int sp__Pop2_Discard(stackP theStack); 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_Pop_Discard(theStack) --theStack->size #define sp_Pop2(theStack, a, b) {sp_Pop(theStack, b);sp_Pop(theStack, a);} #define sp_Pop2_Discard1(theStack, a) {sp_Pop_Discard(theStack);sp_Pop(theStack, a);} #define sp_Pop2_Discard(theStack) {sp_Pop_Discard(theStack);sp_Pop_Discard(theStack);} #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.2.0/c/strbuf.c000066400000000000000000000167231420450503700231100ustar00rootroot00000000000000/* Copyright (c) 1997-2022, John M. Boyer All rights reserved. See the LICENSE.TXT file for licensing information. */ #include "appconst.h" #include "strbuf.h" #include #include /******************************************************************** sb_New() Allocates a string buffer with space for capacity characters, plus one for a null terminator Returns the allocated string buffer, or NULL on error. ********************************************************************/ strBufP sb_New(int capacity) { strBufP theStrBuf; if (capacity < 0) return NULL; theStrBuf = (strBufP) malloc(sizeof(strBuf)); if (theStrBuf != NULL) { theStrBuf->buf = (char *) malloc((capacity+1)*sizeof(char)); if (theStrBuf->buf == NULL) { free(theStrBuf); theStrBuf = NULL; } } if (theStrBuf != NULL) { theStrBuf->capacity = capacity; sb_ClearBuf(theStrBuf); } return theStrBuf; } /******************************************************************** sb_Free() Receives a pointer-pointer to a string buffer. Frees the memory of the string buffer structure and the string buffer it contains. Using the pointer-pointer, sets the pointer to NULL. ********************************************************************/ void sb_Free(strBufP *pStrBuf) { if (pStrBuf != NULL && *pStrBuf != NULL) { (*pStrBuf)->capacity = (*pStrBuf)->size = (*pStrBuf)->readPos = 0; if ((*pStrBuf)->buf != NULL) free((*pStrBuf)->buf); (*pStrBuf)->buf = NULL; free(*pStrBuf); *pStrBuf = NULL; } } /******************************************************************** sb_CleareBuf() The string buffer is changed, if not NULL, to hold an empty string, including setting the size and position to 0. The capacity is not changed. ********************************************************************/ void sb_ClearBuf(strBufP theStrBuf) { if (theStrBuf != NULL) { if (theStrBuf->buf != NULL) theStrBuf->buf[0] = '\0'; theStrBuf->size = theStrBuf->readPos = 0; } } /******************************************************************** sb_Copy() Receives strBuf pointers for a destination and source. Ensures that the destination has the capacity of the source. Copies the source string content into the destination, and sets the size correctly. Returns OK for success, NOTOK on param or memory allocation error. ********************************************************************/ int sb_Copy(strBufP strBufDst, strBufP strBufSrc) { char * tempBuf; strBufP newStrBuf = sb_Duplicate(strBufSrc); if (strBufDst == NULL || strBufSrc == NULL || newStrBuf == NULL) { sb_Free(&newStrBuf); return NOTOK; } tempBuf = strBufDst->buf; strBufDst->buf = newStrBuf->buf; newStrBuf->buf = tempBuf; sb_Free(&newStrBuf); strBufDst->size = strBufSrc->size; strBufDst->capacity = strBufSrc->capacity; return OK; } /******************************************************************** sb_Duplicate() Receives a strBuf pointer. Allocates a new strBuf structure with the same capacity, and initially clears it. If the received strBuf has a non-empty string, then it is copied into the duplicate, and the duplicates size is set correctly. Returns the duplicate, or NULL on memory allocation error. ********************************************************************/ strBufP sb_Duplicate(strBufP theStrBuf) { strBufP newStrBuf = sb_New(theStrBuf->capacity); if (newStrBuf == NULL) return NULL; if (theStrBuf->size > 0) { strcpy(newStrBuf->buf, theStrBuf->buf); newStrBuf->size = theStrBuf->size; } return newStrBuf; } /******************************************************************** sb_ReadSkipWhitespace() Advances the read position managed by the string buffer by skipping any number of whitespace characters, if present. ********************************************************************/ void sb_ReadSkipWhitespace(strBufP theStrBuf) { if (theStrBuf != NULL && theStrBuf->buf != NULL) { while (isspace(theStrBuf->buf[theStrBuf->readPos])) theStrBuf->readPos++; } } /******************************************************************** sb_ReadSkipInteger() Advances the read position managed by the string buffer by skipping an initial negative sign, if present, then skipping any number of numeric digits, if present. ********************************************************************/ void sb_ReadSkipInteger(strBufP theStrBuf) { if (theStrBuf != NULL && theStrBuf->buf != NULL) { if (theStrBuf->buf[theStrBuf->readPos] == '-') theStrBuf->readPos++; while (isdigit(theStrBuf->buf[theStrBuf->readPos])) theStrBuf->readPos++; } } /******************************************************************** sb_ConcatString() Appends the content of string s to the end of the content already in the string buffer. If the append would exceed the capacity of the buffer, then the buffer capacity is first increased. It is increased to one greater than the sum of the current capacity and the new string size or double the capacity (to ensure the memory space is big enough for both strings and linear time performance over many small concatenations). Returns OK on success, NOTOK on error ********************************************************************/ int sb_ConcatString(strBufP theStrBuf, char *s) { int slen = s == NULL ? 0 : strlen(s); if (slen == 0) return OK; if (theStrBuf == NULL || theStrBuf->buf == NULL) return NOTOK; if (theStrBuf->size + slen > theStrBuf->capacity) { int newLen = theStrBuf->size + slen > 2*theStrBuf->capacity ? theStrBuf->size + slen : 2*theStrBuf->capacity; char *newBuf = (char *) malloc((newLen+1)*sizeof(char)); if (newBuf == NULL) return NOTOK; strcpy(newBuf, theStrBuf->buf); free(theStrBuf->buf); theStrBuf->buf = newBuf; theStrBuf->capacity = newLen; } strcpy(theStrBuf->buf + theStrBuf->size, s); theStrBuf->size += slen; return OK; } /******************************************************************** sb_ConcatChar() Converts ch into a one-character string containing ch, then invokes sb_ConcatStr(). Returns Same as sb_ConcatStr() ********************************************************************/ int sb_ConcatChar(strBufP theStrBuf, char ch) { char s[2]; s[0] = ch; s[1] = '\0'; return sb_ConcatString(theStrBuf, s); } /******************************************************************** sb_TakeString() Extracts the buffer string from the received string buffer and returns it. The string buffer is changed to contain a zero length string, so that the buffer can be reused or freed with sb_Free(). Returns NULL on error or a string to be freed by the caller on success. ********************************************************************/ char *sb_TakeString(strBufP theStrBuf) { char * theBuf = NULL; if (theStrBuf == NULL) return NULL; theBuf = theStrBuf->buf; theStrBuf->buf = (char *) malloc(sizeof(char)); theStrBuf->buf[0] = '\0'; theStrBuf->size = theStrBuf->capacity = 0; return theBuf; } edge-addition-planarity-suite-Version_3.0.2.0/c/strbuf.h000066400000000000000000000026751420450503700231160ustar00rootroot00000000000000/* Copyright (c) 1997-2022, John M. Boyer All rights reserved. See the LICENSE.TXT file for licensing information. */ #ifndef STRBUF_H #define STRBUF_H #ifdef __cplusplus extern "C" { #endif // includes mem functions like memcpy #include typedef struct { char *buf; int size, capacity, readPos; } strBuf; typedef strBuf * strBufP; strBufP sb_New(int); void sb_Free(strBufP *); void sb_ClearBuf(strBufP); int sb_Copy(strBufP, strBufP); strBufP sb_Duplicate(strBufP); #define sb_GetFullString(theStrBuf) (theStrBuf->buf) #define sb_GetSize(theStrBuf) (theStrBuf->size) #define sb_GetCapacity(theStrBuf) (theStrBuf->capacity) #define sb_GetReadString(theStrBuf) ( (theStrBuf!=NULL && theStrBuf->buf!=NULL) ? (theStrBuf->buf + theStrBuf->readPos) : NULL) #define sb_GetReadPos(theStrBuf) (theStrBuf->readPos) #define sb_SetReadPos(theStrBuf, theReadPos) { theStrBuf->readPos = theReadPos; } void sb_ReadSkipWhitespace(strBufP); void sb_ReadSkipInteger(strBufP); #define sb_ReadSkipChar(theStrBuf) { theStrBuf->readPos++; } int sb_ConcatString(strBufP, char *); int sb_ConcatChar(strBufP, char); char *sb_TakeString(strBufP); #ifndef SPEED_MACROS // Optimized SPEED_MACROS versions of larger methods are not used in this module #else // Optimized SPEED_MACROS versions of larger methods are not used in this module #endif #ifdef __cplusplus } #endif #endif /* STRBUF_H */ edge-addition-planarity-suite-Version_3.0.2.0/configure.ac000066400000000000000000000017301420450503700234730ustar00rootroot00000000000000AC_INIT(planarity, 3.0.2.0, 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=1 LT_REVISION=0 LT_AGE=1 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_CONFIG_FILES([ Makefile c/samples/Makefile ]) AC_CONFIG_FILES([test-samples.sh], [chmod +x test-samples.sh]) AC_OUTPUT edge-addition-planarity-suite-Version_3.0.2.0/planarity.1000066400000000000000000000042361420450503700232760ustar00rootroot00000000000000.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. edge-addition-planarity-suite-Version_3.0.2.0/test-samples.sh.in000077500000000000000000000004331420450503700245710ustar00rootroot00000000000000#!/bin/sh # # Run "planarity -test" using planarity from the build tree and the # samples from the source tree. # samplesdir="@abs_top_srcdir@/c/samples" planaritydir="@abs_top_builddir@" cd "${planaritydir}" || exit 1 ./planarity -test "${samplesdir}" || exit 2