diff --git a/MANIFEST.in b/MANIFEST.in index f0ada30..48b143a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include planarity_src/c/*.h +recursive-include planarity_src/c *.h include planarity_src/planarity.pyx -include planarmap_src/src/*.h +recursive-include planarmap_src/src *.h include planarmap_src/planarmap.pyx diff --git a/planarity_src/LICENSE.TXT b/planarity_src/LICENSE.TXT new file mode 100644 index 0000000..9cfb4c2 --- /dev/null +++ b/planarity_src/LICENSE.TXT @@ -0,0 +1,48 @@ +The Edge Addition Planarity Suite +Copyright (c) 1997-2025, 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. diff --git a/planarity_src/c/appconst.h b/planarity_src/c/appconst.h deleted file mode 100644 index 4f01cd6..0000000 --- a/planarity_src/c/appconst.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef APPCONST_H -#define APPCONST_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -// When PROFILE is defined, prints out run-time stats on a number of subordinate -// routines in the embedder - -//#define PROFILE -#ifdef PROFILE -#include "platformTime.h" -#endif - -/* Define DEBUG to get additional debugging. The default is to define it when MSC does */ - -#ifdef _DEBUG -#define DEBUG -#endif - -/* Some low-level functions are replaced by faster macros, except when debugging */ - -#define SPEED_MACROS -#ifdef DEBUG -#undef SPEED_MACROS -#endif - -/* Return status values; OK/NOTOK behave like Boolean true/false, - not like program exit codes. */ - -#define OK 1 -#define NOTOK 0 - -#ifdef DEBUG -#undef NOTOK -extern int debugNOTOK(); -#include -#define NOTOK (printf("NOTOK on Line %d of %s\n", __LINE__, __FILE__), debugNOTOK()) -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef NULL -#define NULL 0L -#endif - -/* Array indices are used as pointers, and NIL means bad pointer */ - -// This definition is used with 1-based array indexing -//#define NIL 0 -//#define NIL_CHAR 0x00 - -// This definition is used in combination with 0-based array indexing -#define NIL -1 -#define NIL_CHAR 0xFF - -/* Defines fopen strings for reading and writing text files on PC and UNIX */ - -#ifdef WINDOWS -#define READTEXT "rt" -#define WRITETEXT "wt" -#else -#define READTEXT "r" -#define WRITETEXT "w" -#endif - -/******************************************************************** - A few simple integer selection macros - ********************************************************************/ - -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) - -#define MIN3(x, y, z) MIN(MIN((x), (y)), MIN((y), (z))) -#define MAX3(x, y, z) MAX(MAX((x), (y)), MAX((y), (z))) - -#endif diff --git a/planarity_src/c/graph.h b/planarity_src/c/graph.h index 256dc09..3b9a9dd 100644 --- a/planarity_src/c/graph.h +++ b/planarity_src/c/graph.h @@ -1,172 +1,28 @@ -#ifndef GRAPH_H -#define GRAPH_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "graphStructures.h" - -#include "graphExtensions.h" - -/////////////////////////////////////////////////////////////////////////////// -// Definitions for higher-order operations at the vertex, edge and graph levels -/////////////////////////////////////////////////////////////////////////////// - -graphP gp_New(void); - -int gp_InitGraph(graphP theGraph, int N); -void gp_ReinitializeGraph(graphP theGraph); -int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph); -int gp_CopyGraph(graphP dstGraph, graphP srcGraph); -graphP gp_DupGraph(graphP theGraph); - -int gp_CreateRandomGraph(graphP theGraph); -int gp_CreateRandomGraphEx(graphP theGraph, int numEdges); - -void gp_Free(graphP *pGraph); - -int gp_Read(graphP theGraph, char *FileName); -#define WRITE_ADJLIST 1 -#define WRITE_ADJMATRIX 2 -#define WRITE_DEBUGINFO 3 -int gp_Write(graphP theGraph, char *FileName, int Mode); - -int gp_IsNeighbor(graphP theGraph, int u, int v); -int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v); -int gp_GetVertexDegree(graphP theGraph, int v); -int gp_GetVertexInDegree(graphP theGraph, int v); -int gp_GetVertexOutDegree(graphP theGraph, int v); - -int gp_GetArcCapacity(graphP theGraph); -int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink); -int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink, - int v, int e_v, int e_vlink); - -void gp_HideEdge(graphP theGraph, int e); -void gp_RestoreEdge(graphP theGraph, int e); -int gp_HideVertex(graphP theGraph, int vertex); -int gp_DeleteEdge(graphP theGraph, int e, int nextLink); - -int gp_ContractEdge(graphP theGraph, int e); -int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore); -int gp_RestoreVertices(graphP theGraph); - -int gp_CreateDFSTree(graphP theGraph); -int gp_SortVertices(graphP theGraph); -int gp_LowpointAndLeastAncestor(graphP theGraph); -int gp_PreprocessForEmbedding(graphP theGraph); - -int gp_Embed(graphP theGraph, int embedFlags); -int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult); - -/* Possible Flags for gp_Embed. The planar and outerplanar settings are supported - natively. The rest require extension modules. */ - -#define EMBEDFLAGS_PLANAR 1 -#define EMBEDFLAGS_OUTERPLANAR 2 - -#define EMBEDFLAGS_DRAWPLANAR (4|EMBEDFLAGS_PLANAR) - -#define EMBEDFLAGS_SEARCHFORK23 (16|EMBEDFLAGS_OUTERPLANAR) -#define EMBEDFLAGS_SEARCHFORK4 (32|EMBEDFLAGS_OUTERPLANAR) -#define EMBEDFLAGS_SEARCHFORK33 (64|EMBEDFLAGS_PLANAR) - -#define EMBEDFLAGS_SEARCHFORK5 (128|EMBEDFLAGS_PLANAR) - -#define EMBEDFLAGS_MAXIMALPLANARSUBGRAPH 256 -#define EMBEDFLAGS_PROJECTIVEPLANAR 512 -#define EMBEDFLAGS_TOROIDAL 1024 - -/* If LOGGING is defined, then write to the log, otherwise no-op - By default, neither release nor DEBUG builds including LOGGING. - Logging is useful for seeing details of how various algorithms - handle a particular graph. */ - -//#define LOGGING -#ifdef LOGGING - -#define gp_LogLine _LogLine -#define gp_Log _Log - -void _LogLine(char *Line); -void _Log(char *Line); - -#define gp_MakeLogStr1 _MakeLogStr1 -#define gp_MakeLogStr2 _MakeLogStr2 -#define gp_MakeLogStr3 _MakeLogStr3 -#define gp_MakeLogStr4 _MakeLogStr4 -#define gp_MakeLogStr5 _MakeLogStr5 - -char *_MakeLogStr1(char *format, int); -char *_MakeLogStr2(char *format, int, int); -char *_MakeLogStr3(char *format, int, int, int); -char *_MakeLogStr4(char *format, int, int, int, int); -char *_MakeLogStr5(char *format, int, int, int, int, int); - -#else -#define gp_LogLine(Line) -#define gp_Log(Line) -#define gp_MakeLogStr1(format, one) -#define gp_MakeLogStr2(format, one, two) -#define gp_MakeLogStr3(format, one, two, three) -#define gp_MakeLogStr4(format, one, two, three, four) -#define gp_MakeLogStr5(format, one, two, three, four, five) -#endif - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef HELPERSTUB_GRAPH_H +#define HELPERSTUB_GRAPH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +// NOTE: This helper stub has been added for backwards compatibility to +// support downstream consumers who expect graph.h to be at the +// root of the installed planarity headers directory. Future downstream +// consumers are advised to include the graphLib.h helper stub instead +// because it offers access to all features in the graph library of +// the planarity project. + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "c/graphLib/graph.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/planarity_src/c/graphColorVertices.c b/planarity_src/c/graphColorVertices.c deleted file mode 100644 index 21fc4ff..0000000 --- a/planarity_src/c/graphColorVertices.c +++ /dev/null @@ -1,441 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graphColorVertices.h" -#include "graphColorVertices.private.h" - -extern int COLORVERTICES_ID; - -#include "graph.h" - -#include -#include -#include - -extern void _ClearVertexVisitedFlags(graphP theGraph, int); -extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); - -extern void _ColorVertices_Reinitialize(ColorVerticesContext *context); - -/* Private functions exported to system */ - -void _AddVertexToDegList(ColorVerticesContext *context, graphP theGraph, int v, int deg); -void _RemoveVertexFromDegList(ColorVerticesContext *context, graphP theGraph, int v, int deg); -int _AssignColorToVertex(ColorVerticesContext *context, graphP theGraph, int v); - -/* Private functions */ - -int _GetVertexToReduce(ColorVerticesContext *context, graphP theGraph); -int _IsConstantTimeContractible(ColorVerticesContext *context, int v); -int _GetContractibleNeighbors(ColorVerticesContext *context, int v, int *pu, int *pw); - -/******************************************************************** - gp_ColorVertices() - - This is the entry point for requesting a vertex coloring by the - the minimum degree selection method. - - The call pattern is to simply invoke this function on a graph to - color it or recolor it after some mutations. It will invoke - gp_AttachColorVertices() to attach the auxiliary data needed to - performing the coloring, and the attachment short-circuits if - already done. - - After calling this function, call gp_ColorVertices_GetColors() to - obtain the colors or gp_Write() to save the colors. To read a saved - coloring, use gp_AttachColorVertices() then gp_Read(). - - Returns OK on success, NOTOK on failure - ********************************************************************/ -#include "platformTime.h" - -int gp_ColorVertices(graphP theGraph) -{ - ColorVerticesContext *context = NULL; - int v, deg; - int u=0, w=0, contractible; - - // Attach the algorithm if it is not already attached - if (gp_AttachColorVertices(theGraph) != OK) - return NOTOK; - - // Ensure there is enough stack to perform this operation. - // At a maximum, the graph reduction will push 7N+M integers. - // One integer is pushed per edge that is hidden. Plus, whether - // a vertex is hidden or identified with another vertex, 7 integers - // are used to store enough information to restore it. - if (sp_NonEmpty(theGraph->theStack)) - return NOTOK; - - if (sp_GetCapacity(theGraph->theStack) < 7*theGraph->N + theGraph->M) - { - stackP newStack = sp_New(7*theGraph->N + theGraph->M); - if (newStack == NULL) - return NOTOK; - sp_Free(&theGraph->theStack); - theGraph->theStack = newStack; - } - - // Get the extension context and reinitialize it if necessary - gp_FindExtension(theGraph, COLORVERTICES_ID, (void *)&context); - - if (context->color[0] > -1) - _ColorVertices_Reinitialize(context); - - // Initialize the degree lists, and provide a color for any trivial vertices - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - deg = gp_GetVertexDegree(theGraph, v); - _AddVertexToDegList(context, theGraph, v, deg); - - if (deg == 0) - context->color[v] = 0; - } - - // Initialize the vertex visited flags so they can be used during reductions - _ClearVertexVisitedFlags(theGraph, FALSE); - - // Reduce the graph using minimum degree selection - while (context->numVerticesToReduce > 0) - { - v = _GetVertexToReduce(context, theGraph); - - // Find out if v is contractible and the neighbors to contract - contractible = _GetContractibleNeighbors(context, v, &u, &w); - - // Remove the vertex from the graph. This calls the fpHideEdge - // overload, which performs the correct _RemoveVertexFromDegList() - // and _AddVertexToDegList() operations on v and its neighbors. - if (gp_HideVertex(theGraph, v) != OK) - return NOTOK; - - // If v was contractibile, then identify u and w - if (contractible) - { - if (gp_IdentifyVertices(theGraph, u, w, NIL) != OK) - return NOTOK; - } - } - - // Restore the graph one vertex at a time, coloring each vertex distinctly - // from its neighbors as it is restored. - context->colorDetector = (int *) calloc(theGraph->N, sizeof(int)); - if (context->colorDetector == NULL) - return NOTOK; - - if (gp_RestoreVertices(theGraph) != OK) - return NOTOK; - - free(context->colorDetector); - context->colorDetector = NULL; - - return OK; -} - -/******************************************************************** - _AddVertexToDegList() - - This function adds vertex v to degree list deg. - The current method simply appends the vertex to the degree list. - - This method will be improved later to handle the degree 5 list - specially by prepending those degree 5 vertices that have two - non-adjacent neighbors with a constant degree bound. These vertices - can be specially handled by identifying the non-adjacent neighbors - during reduction so that the neighborhood of v receives only three - colors. This ensures that all planar graphs use at most 5 colors. - Matula, Shiloach and Tarjan (1980) introduced this contraction - method, and the tighter degree bound on the neighbors used in this - implementation is due to Frederickson (1984). - ********************************************************************/ - -void _AddVertexToDegList(ColorVerticesContext *context, graphP theGraph, int v, int deg) -{ - if (deg > 0) - { - if (_IsConstantTimeContractible(context, v)) - context->degListHeads[deg] = LCPrepend(context->degLists, context->degListHeads[deg], v); - else - context->degListHeads[deg] = LCAppend(context->degLists, context->degListHeads[deg], v); - - context->numVerticesToReduce++; - } - context->degree[v] = deg; -} - -/******************************************************************** - _GetVertexDegree() - ********************************************************************/ - -int _GetVertexDegree(ColorVerticesContext *context, int v) -{ - return context->degree[v]; - - // We cache vertex degree values because the API function is O(deg(v)), - // which would make this algorithm implementation have quadratic behavior - // in the worst case - // - // return gp_GetVertexDegree(context->theGraph, v); -} - -/******************************************************************** - _IsConstantTimeContractible() - Wrapper function that just returns the result of _GetContractibleNeighbors() - Return TRUE if v is degree 5 and has a pair of non-adjacent neighbors - of degree 7 or lower; FALSE otherwise. - ********************************************************************/ - -int _IsConstantTimeContractible(ColorVerticesContext *context, int v) -{ - int u, w; - return _GetContractibleNeighbors(context, v, &u, &w); -} - -/******************************************************************** - _GetContractibleNeighbors() - Wrapper function that just returns the result of _GetContractibleNeighbors() - - This function returns TRUE if the vertex v is degree 5 and has two - non-adjacent neighbors of degree at most 7. In 1980, Matula, Shiloach - and Tarjan proved the sequential contraction method of five-coloring - planar graphs could run in linear time based on deleting any vertices - less than degree 5 and, if none exist, contracting a degree 5 vertex - with two non-adjacent neighbors of degree at most 11. In 1984, - Greg N. Frederickson improved the result to 7. - - When a vertex is being added to the degree list, it is appended - unless this function returns TRUE, in which case it is placed - at the front of the degree 5 list. - When a vertex is removed from a degree list for reduction, it is - tested again, and if this function returns TRUE, then two non-adjacent - neighbors of degree at most 7 are found. The vertex is hidden in - either case, but if the neighbors were found, then they are - identified. In the recursion, the neighbors will get the same - color so that when the vertex is restored, its neighborhood has at - most four colors. The vertex takes the fifth color. - Hence, planar graphs are colored with at most five colors. Non-planar - graphs are still colored, but perhaps with more than five colors since - the 5 list may become empty or may not start with a constant time - contractible vertex (in which case we stick with the constant time - per edge deletion only). - - This function operates in constant time, so it only finds a pair of - contractible neighbors for degree 5 vertices, it determines the degree - of all neighbors in constant time, it determines whether each pair of - low degree neighbors is non-adjacent in constant time, and the degree - bound on the pair of neighbors returned ensures that they can be - identified (including removal of duplicate edges) in constant time. - - Return TRUE if v is degree 5 and has a pair of non-adjacent neighbors - of degree 7 or lower; FALSE otherwise. - - Also returns the two neighbors found if TRUE is returned. The pointer - variables are not altered in the FALSE case. - ********************************************************************/ - -int _GetContractibleNeighbors(ColorVerticesContext *context, int v, int *pu, int *pw) -{ - int lowDegreeNeighbors[5], i, j, n=0, e; - graphP theGraph = context->theGraph; - - // This method is only applicable to degree 5 vertices - if (_GetVertexDegree(context, v) != 5) - return FALSE; - - // Get all neighbors of degree at most 7 - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (_GetVertexDegree(context, gp_GetNeighbor(theGraph, e)) <= 7) - lowDegreeNeighbors[n++] = gp_GetNeighbor(theGraph, e); - e = gp_GetNextArc(theGraph, e); - } - - // Seek the pair of *non-adjacent* low degree neighbors - for (i=0; i < (n-1); i++) - for (j=i+1; j < n; j++) - if (!gp_IsNeighbor(theGraph, lowDegreeNeighbors[i], lowDegreeNeighbors[j])) - { - *pu = lowDegreeNeighbors[i]; - *pw = lowDegreeNeighbors[j]; - return TRUE; - } - - // The desired pair of neighbors was not found - return FALSE; -} - - -/******************************************************************** - _RemoveVertexFromDegList() - ********************************************************************/ - -void _RemoveVertexFromDegList(ColorVerticesContext *context, graphP theGraph, int v, int deg) -{ - if (deg > 0) - { - context->degListHeads[deg] = LCDelete(context->degLists, context->degListHeads[deg], v); - context->numVerticesToReduce--; - } -} - -/******************************************************************** - _GetVertexToReduce() - ********************************************************************/ - -int _GetVertexToReduce(ColorVerticesContext *context, graphP theGraph) -{ - int v = NIL, deg; - - for (deg = 1; deg < theGraph->N; deg++) - { - if (gp_IsVertex(context->degListHeads[deg])) - { - // Get the first vertex in the list - v = context->degListHeads[deg]; - break; - } - } - - return v; -} - -/******************************************************************** - _AssignColorToVertex() - ********************************************************************/ - -int _AssignColorToVertex(ColorVerticesContext *context, graphP theGraph, int v) -{ - int e, w, color; - - // Run the neighbor list of v and flag all the colors in use - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - w = gp_GetNeighbor(theGraph, e); - context->colorDetector[context->color[w]] = 1; - - e = gp_GetNextArc(theGraph, e); - } - - // Find the least numbered unused color and assign it to v - // Note that this loop never runs more than deg(v) steps - for (color = 0; color < theGraph->N; color++) - { - if (context->colorDetector[color] == 0) - { - context->color[v] = color; - if (context->highestColorUsed < color) - context->highestColorUsed = color; - break; - } - } - - if (context->color[v] < 0) - return NOTOK; - - // Run the neighbor list of v and unflag all the colors in use - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - w = gp_GetNeighbor(theGraph, e); - context->colorDetector[context->color[w]] = 0; - - e = gp_GetNextArc(theGraph, e); - } - - return OK; -} - -/******************************************************************** - gp_GetNumColorsUsed() - ********************************************************************/ - -int gp_GetNumColorsUsed(graphP theGraph) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - return context == NULL ? 0 : context->highestColorUsed+1; -} - -/******************************************************************** - gp_ColorVerticesIntegrityCheck() - ********************************************************************/ - -int gp_ColorVerticesIntegrityCheck(graphP theGraph, graphP origGraph) -{ - int v, w, e; - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (theGraph == NULL || origGraph == NULL || context == NULL) - return NOTOK; - - if (gp_GetNumColorsUsed(theGraph) <= 0 && theGraph->M > 0) - return NOTOK; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - return NOTOK; - - if (_TestSubgraph(origGraph, theGraph) != TRUE) - return NOTOK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - w = gp_GetNeighbor(theGraph, e); - if (context->color[v] < 0 || context->color[v] == context->color[w]) - return NOTOK; - - e = gp_GetNextArc(theGraph, e); - } - } - - return OK; -} diff --git a/planarity_src/c/graphColorVertices.h b/planarity_src/c/graphColorVertices.h deleted file mode 100644 index e17aa01..0000000 --- a/planarity_src/c/graphColorVertices.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef GRAPH_COLORVERTICES_H -#define GRAPH_COLORVERTICES_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graphStructures.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define COLORVERTICES_NAME "ColorVertices" - -int gp_AttachColorVertices(graphP theGraph); -int gp_DetachColorVertices(graphP theGraph); - -int gp_ColorVertices(graphP theGraph); -int gp_GetNumColorsUsed(graphP theGraph); -int gp_ColorVerticesIntegrityCheck(graphP theGraph, graphP origGraph); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/planarity_src/c/graphColorVertices.private.h b/planarity_src/c/graphColorVertices.private.h deleted file mode 100644 index 421a804..0000000 --- a/planarity_src/c/graphColorVertices.private.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef GRAPH_COLORVERTICES_PRIVATE_H -#define GRAPH_COLORVERTICES_PRIVATE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - // Helps distinguish initialize from re-initialize - int initialized; - - // The graph that this context augments - graphP theGraph; - - // Overloaded function pointers - graphFunctionTable functions; - - // Data structures, unique to this extension, for managing lists of - // vertices by degree (e.g. all vertices of degree K in list K), and - // for storing each vertex color (e.g. vertex K has color[K]) - listCollectionP degLists; - int *degListHeads; - int *degree; - int *color; - int numVerticesToReduce, highestColorUsed; - - int *colorDetector; - -} ColorVerticesContext; - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/planarity_src/c/graphColorVertices_Extensions.c b/planarity_src/c/graphColorVertices_Extensions.c deleted file mode 100644 index 031451e..0000000 --- a/planarity_src/c/graphColorVertices_Extensions.c +++ /dev/null @@ -1,642 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include - -#include "graphColorVertices.private.h" -#include "graphColorVertices.h" - -extern void _AddVertexToDegList(ColorVerticesContext *context, graphP theGraph, int v, int deg); -extern void _RemoveVertexFromDegList(ColorVerticesContext *context, graphP theGraph, int v, int deg); -extern int _AssignColorToVertex(ColorVerticesContext *context, graphP theGraph, int v); -extern int _GetVertexDegree(ColorVerticesContext *context, int v); - -/* Forward declarations of local functions */ - -void _ColorVertices_ClearStructures(ColorVerticesContext *context); -int _ColorVertices_CreateStructures(ColorVerticesContext *context); -int _ColorVertices_InitStructures(ColorVerticesContext *context); - -/* Forward declarations of overloading functions */ - -int _ColorVertices_InitGraph(graphP theGraph, int N); -void _ColorVertices_ReinitializeGraph(graphP theGraph); - -int _ColorVertices_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize); -int _ColorVertices_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize); - -void _ColorVertices_HideEdge(graphP theGraph, int e); -int _ColorVertices_IdentifyVertices(graphP theGraph, int u, int v, int eBefore); -int _ColorVertices_RestoreVertex(graphP theGraph); - -/* Forward declarations of functions used by the extension system */ - -void *_ColorVertices_DupContext(void *pContext, void *theGraph); -void _ColorVertices_FreeContext(void *); - -/**************************************************************************** - * COLORVERTICES_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 COLORVERTICES_ID = 0; - -/**************************************************************************** - gp_AttachColorVertices() - - This function adjusts the graph data structure to attach the graph - vertex coloringfeature. - - To activate this feature, call gp_ColorVertices(). - - 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 vertex coloring. 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_AttachColorVertices(graphP theGraph) -{ - ColorVerticesContext *context = NULL; - - // If the vertex coloring feature has already been attached to the graph, - // then there is no need to attach it again - gp_FindExtension(theGraph, COLORVERTICES_ID, (void *)&context); - if (context != NULL) - { - return OK; - } - - // Allocate a new extension context - context = (ColorVerticesContext *) malloc(sizeof(ColorVerticesContext)); - 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.fpInitGraph = _ColorVertices_InitGraph; - context->functions.fpReinitializeGraph = _ColorVertices_ReinitializeGraph; - - context->functions.fpReadPostprocess = _ColorVertices_ReadPostprocess; - context->functions.fpWritePostprocess = _ColorVertices_WritePostprocess; - - context->functions.fpHideEdge = _ColorVertices_HideEdge; - context->functions.fpIdentifyVertices = _ColorVertices_IdentifyVertices; - context->functions.fpRestoreVertex = _ColorVertices_RestoreVertex; - - _ColorVertices_ClearStructures(context); - - // Store the context, including the data structure and the - // function pointers, as an extension of the graph - if (gp_AddExtension(theGraph, &COLORVERTICES_ID, (void *) context, - _ColorVertices_DupContext, _ColorVertices_FreeContext, - &context->functions) != OK) - { - _ColorVertices_FreeContext(context); - return NOTOK; - } - - // Create the algorithm-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 (_ColorVertices_CreateStructures(context) != OK || - _ColorVertices_InitStructures(context) != OK) - { - _ColorVertices_FreeContext(context); - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - gp_DetachColorVertices() - ********************************************************************/ - -int gp_DetachColorVertices(graphP theGraph) -{ - return gp_RemoveExtension(theGraph, COLORVERTICES_ID); -} - -/******************************************************************** - _ColorVertices_ClearStructures() - ********************************************************************/ - -void _ColorVertices_ClearStructures(ColorVerticesContext *context) -{ - if (!context->initialized) - { - // Before initialization, the pointers are stray, not NULL - // Once NULL or allocated, free() or LCFree() can do the job - context->degLists = NULL; - context->degListHeads = NULL; - context->degree = NULL; - context->color = NULL; - context->numVerticesToReduce = 0; - context->highestColorUsed = -1; - context->colorDetector = NULL; - - context->initialized = 1; - } - else - { - if (context->degLists != NULL) - { - LCFree(&context->degLists); - } - if (context->degListHeads != NULL) - { - free(context->degListHeads); - context->degListHeads = NULL; - } - if (context->degree != NULL) - { - free(context->degree); - context->degree = NULL; - } - if (context->color != NULL) - { - free(context->color); - context->color = NULL; - } - context->numVerticesToReduce = 0; - context->highestColorUsed = -1; - context->colorDetector = NULL; - } -} - -/******************************************************************** - _ColorVertices_CreateStructures() - Create uninitialized structures for the vertex and edge - levels, and initialized structures for the graph level - ********************************************************************/ -int _ColorVertices_CreateStructures(ColorVerticesContext *context) -{ - graphP theGraph = context->theGraph; - int VIsize = gp_PrimaryVertexIndexBound(theGraph); - int v; - - if (theGraph->N <= 0) - return NOTOK; - - if ((context->degLists = LCNew(VIsize)) == NULL || - (context->degListHeads = (int *) malloc(VIsize*sizeof(int))) == NULL || - (context->degree = (int *) malloc(VIsize*sizeof(int))) == NULL || - (context->color = (int *) malloc(VIsize*sizeof(int))) == NULL - ) - { - return NOTOK; - } - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - context->degListHeads[v] = NIL; - context->degree[v] = 0; - context->color[v] = 0; - } - - context->numVerticesToReduce = 0; - context->highestColorUsed = -1; - context->colorDetector = NULL; - - return OK; -} - -/******************************************************************** - _ColorVertices_InitStructures() - Intended to be called when N>0. - Initializes vertex and edge levels only. Graph level is - initialized by _CreateStructures(). - ********************************************************************/ -int _ColorVertices_InitStructures(ColorVerticesContext *context) -{ - return OK; -} - -/******************************************************************** - _ColorVertices_DupContext() - ********************************************************************/ - -void *_ColorVertices_DupContext(void *pContext, void *pGraph) -{ - ColorVerticesContext *context = (ColorVerticesContext *) pContext; - ColorVerticesContext *newContext = (ColorVerticesContext *) malloc(sizeof(ColorVerticesContext)); - graphP theGraph = (graphP) pGraph; - - if (newContext != NULL) - { - *newContext = *context; - newContext->theGraph = theGraph; - - newContext->initialized = 0; - _ColorVertices_ClearStructures(newContext); - if (theGraph->N > 0) - { - int v; - - if (_ColorVertices_CreateStructures(newContext) != OK) - { - _ColorVertices_FreeContext(newContext); - return NULL; - } - - // Initialize custom data structures by copying - LCCopy(newContext->degLists, context->degLists); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - newContext->degListHeads[v] = context->degListHeads[v]; - newContext->degree[v] = context->degree[v]; - newContext->color[v] = context->color[v]; - } - newContext->numVerticesToReduce = context->numVerticesToReduce; - newContext->highestColorUsed = context->highestColorUsed; - newContext->colorDetector = NULL; - } - } - - return newContext; -} - -/******************************************************************** - _ColorVertices_FreeContext() - ********************************************************************/ - -void _ColorVertices_FreeContext(void *pContext) -{ - ColorVerticesContext *context = (ColorVerticesContext *) pContext; - - _ColorVertices_ClearStructures(context); - free(pContext); -} - -/******************************************************************** - ********************************************************************/ - -int _ColorVertices_InitGraph(graphP theGraph, int N) -{ - ColorVerticesContext *context = NULL; - gp_FindExtension(theGraph, COLORVERTICES_ID, (void *)&context); - - if (context == NULL) - return NOTOK; - - theGraph->N = N; - if (theGraph->arcCapacity == 0) - theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N; - - if (_ColorVertices_CreateStructures(context) != OK || - _ColorVertices_InitStructures(context) != OK) - return NOTOK; - - context->functions.fpInitGraph(theGraph, N); - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -void _ColorVertices_Reinitialize(ColorVerticesContext *context) -{ - graphP theGraph = context->theGraph; - int v; - - LCReset(context->degLists); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - context->degListHeads[v] = NIL; - context->degree[v] = 0; - context->color[v] = 0; - } - context->numVerticesToReduce = 0; - context->highestColorUsed = -1; - context->colorDetector = NULL; -} - -/******************************************************************** - ********************************************************************/ - -void _ColorVertices_ReinitializeGraph(graphP theGraph) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (context != NULL) - { - // Reinitialize the graph - context->functions.fpReinitializeGraph(theGraph); - - // Reinitialize graph-level data extensions - _ColorVertices_Reinitialize(context); - } -} - -/******************************************************************** - ********************************************************************/ - -int _ColorVertices_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (context != NULL) - { - if (context->functions.fpReadPostprocess(theGraph, extraData, extraDataSize) != OK) - return NOTOK; - - else if (extraData != NULL && extraDataSize > 0) - { - int v, tempInt; - char line[64], tempChar; - - sprintf(line, "<%s>", COLORVERTICES_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", &tempInt, &tempChar, &context->color[v]); - - extraData = strchr(extraData, '\n') + 1; - } - } - - return OK; - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _ColorVertices_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (context != NULL) - { - if (context->functions.fpWritePostprocess(theGraph, pExtraData, pExtraDataSize) != OK) - return NOTOK; - else - { - char line[32]; - int maxLineSize = 32, extraDataPos = 0, v; - char *extraData = (char *) malloc((theGraph->N + 2) * maxLineSize * sizeof(char)); - int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(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", COLORVERTICES_NAME); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - sprintf(line, "%d: %d\n", v-zeroBasedOffset, context->color[v]); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - } - - sprintf(line, "\n", COLORVERTICES_NAME); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - - *pExtraData = (void *) extraData; - *pExtraDataSize = extraDataPos * sizeof(char); - } - - return OK; - } - - return NOTOK; -} - -/******************************************************************** - _ColorVertices_HideEdge() - - An overload to perform the degree list updates for the edge endpoints. - This routine also covers the work done by _HideVertex() and part of - the work done by _ContractEdge() and _IdentifyVertices(). - ********************************************************************/ -void _ColorVertices_HideEdge(graphP theGraph, int e) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (context != NULL) - { - int u, v, udeg, vdeg; - - // Get the endpoint vertices of the edge - u = gp_GetNeighbor(theGraph, e); - v = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, e)); - - // Get the degrees of the vertices - udeg = _GetVertexDegree(context, u); - vdeg = _GetVertexDegree(context, v); - - // Remove them from the degree lists that contain them - _RemoveVertexFromDegList(context, theGraph, u, udeg); - _RemoveVertexFromDegList(context, theGraph, v, vdeg); - - // Hide the edge - context->functions.fpHideEdge(theGraph, e); - - // Decrement the degrees of the endpoint vertices - udeg--; - vdeg--; - - // Add them to the new degree lists - _AddVertexToDegList(context, theGraph, u, udeg); - _AddVertexToDegList(context, theGraph, v, vdeg); - } -} - -/******************************************************************** - _ColorVertices_IdentifyVertices() - - An overload to perform degree list updates corresponding to the - transfer of v's adjacency list into u. Since the common edges are - removed using _HideEdge(), this routine only accounts for those - edges of v that create new neighbors for u. These are indicated on - the top of the stack after the base routine is called. - This routine also does part of the work of _ContractEdge(), which - simply combines _HideEdge() and _IdentifyVertices(). - ********************************************************************/ - -int _ColorVertices_IdentifyVertices(graphP theGraph, int u, int v, int eBefore) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (context != NULL) - { - int e_v_last, e_v_first; - - // First, identify u and v. No point in taking v's degree beforehand - // because some of its incident edges may indicate neighbors of u. This - // causes v to be moved to a lower degree list than deg(v). - if (context->functions.fpIdentifyVertices(theGraph, u, v, eBefore) != OK) - return NOTOK; - - // The edges transferred from v to u are indicated on the top of the - // stack, which looks like this after identifying u and v: - // ... e_u_succ e_v_last e_v_first e_u_pred u v - e_v_first = sp_Get(theGraph->theStack, sp_GetCurrentSize(theGraph->theStack)-4); - e_v_last = sp_Get(theGraph->theStack, sp_GetCurrentSize(theGraph->theStack)-5); - - // We count the number of edges K transferred from v to u after the - // common edges were hidden - if (gp_IsArc(e_v_first)) - { - int e, K, degu; - - for (e=e_v_first, K=1; e != e_v_last; e=gp_GetNextArc(theGraph, e)) - K++; - - // Remove v from the degree list K. During IdentifyVertices(), if v had any - // common edges with u, they were "hidden", which reduced the degree of v to K. - _RemoveVertexFromDegList(context, theGraph, v, K); - - // We move u from degree list deg(u)-K to degree list deg(u) - degu = gp_GetVertexDegree(theGraph, u); - _RemoveVertexFromDegList(context, theGraph, u, degu-K); - _AddVertexToDegList(context, theGraph, u, degu); - } - - return OK; - } - - return NOTOK; -} - -/******************************************************************** - _ColorVertices_RestoreVertex() - - An overload to color the vertex distinctly from its neighbors once - it is restored. - ********************************************************************/ - -int _ColorVertices_RestoreVertex(graphP theGraph) -{ - ColorVerticesContext *context = (ColorVerticesContext *) gp_GetExtension(theGraph, COLORVERTICES_ID); - - if (context != NULL) - { - int u, v; - - // Read the stack to figure out which vertex is being restored - u = sp_Get(theGraph->theStack, sp_GetCurrentSize(theGraph->theStack)-2); - v = sp_Get(theGraph->theStack, sp_GetCurrentSize(theGraph->theStack)-1); - - // Restore the vertex - if (context->functions.fpRestoreVertex(theGraph) != OK) - return NOTOK; - - // If the restored vertex v was hidden, then give it a color distinct from its neighbors - // Note that u is not a vertex in this case - if (gp_IsNotVertex(u)) - { - if (_AssignColorToVertex(context, theGraph, v) != OK) - return NOTOK; - - if (context->color[v] < 0) - return NOTOK; - } - - // Else if the restored vertex v was identified, then give v the same color as the - // vertex u with which it was identified. - else - { - context->color[v] = context->color[u]; - } - - return OK; - } - - return NOTOK; -} diff --git a/planarity_src/c/graphDFSUtils.c b/planarity_src/c/graphDFSUtils.c deleted file mode 100644 index 1efd380..0000000 --- a/planarity_src/c/graphDFSUtils.c +++ /dev/null @@ -1,496 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#define GRAPHDFSUTILS_C - -#include "graph.h" - -extern void _ClearVertexVisitedFlags(graphP theGraph, int); - -/******************************************************************** - gp_CreateDFSTree - Assigns Depth First Index (DFI) to each vertex. Also records parent - of each vertex in the DFS tree, and marks DFS tree edges that go from - parent to child. Forward arc cycle edges are also distinguished from - edges leading from a DFS tree descendant to an ancestor-- both DFS tree - edges and back arcs. The forward arcs are moved to the end of the - adjacency list to make the set easier to find and process. - - NOTE: This is a utility function provided for general use of the graph - library. The core planarity algorithm uses its own DFS in order to build - up related data structures at the same time as the DFS tree is created. - ********************************************************************/ - -#include "platformTime.h" - -int gp_CreateDFSTree(graphP theGraph) -{ -stackP theStack; -int N, DFI, v, uparent, u, e; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - if (theGraph==NULL) return NOTOK; - if (theGraph->internalFlags & FLAGS_DFSNUMBERED) return OK; - - gp_LogLine("\ngraphDFSUtils.c/gp_CreateDFSTree() start"); - - N = theGraph->N; - theStack = theGraph->theStack; - -/* There are 2M edge records (arcs) and for each we can push 2 integers, - so a stack of 2 * arcCapacity integers suffices. - This is already in theGraph structure, so we make sure it's empty, - then clear all visited flags in prep for the Depth first search. */ - - if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph)) - return NOTOK; - - sp_ClearStack(theStack); - - _ClearVertexVisitedFlags(theGraph, FALSE); - -/* This outer loop causes the connected subgraphs of a disconnected - graph to be numbered */ - - for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++) - { - if (gp_IsNotDFSTreeRoot(theGraph, v)) - continue; - - sp_Push2(theStack, NIL, NIL); - while (sp_NonEmpty(theStack)) - { - sp_Pop2(theStack, uparent, e); - u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e); - - if (!gp_GetVertexVisited(theGraph, u)) - { - gp_LogLine(gp_MakeLogStr3("V=%d, DFI=%d, Parent=%d", u, DFI, uparent)); - - gp_SetVertexVisited(theGraph, u); - gp_SetVertexIndex(theGraph, u, DFI++); - gp_SetVertexParent(theGraph, u, uparent); - if (gp_IsArc(e)) - { - gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT); - } - - /* Push edges to all unvisited neighbors. These will be either - tree edges to children or forward arcs of back edges */ - - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - sp_Push2(theStack, u, e); - e = gp_GetNextArc(theGraph, e); - } - } - else - { - // If the edge leads to a visited vertex, then it is - // the forward arc of a back edge. - gp_SetEdgeType(theGraph, e, EDGE_TYPE_FORWARD); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_BACK); - } - } - } - - gp_LogLine("graphDFSUtils.c/gp_CreateDFSTree() end\n"); - - theGraph->internalFlags |= FLAGS_DFSNUMBERED; - -#ifdef PROFILE -platform_GetTime(end); -printf("DFS in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - gp_SortVertices() - Once depth first numbering has been applied to the graph, the index - member of each vertex contains the DFI. This routine can reorder the - vertices in linear time so that they appear in ascending order by DFI. - Note that the index field is then used to store the original number - of the vertex. Therefore, a second call to this method will put the - vertices back to the original order and put the DFIs back into the - index fields of the vertices. - - NOTE: This function is used by the core planarity algorithm, once its - custom DFS has assigned DFIs to the vertices. Once gp_Embed() has - finished creating an embedding or obstructing subgraph, this function - can be called to restore the original vertex numbering, if needed. - ********************************************************************/ - -int gp_SortVertices(graphP theGraph) -{ - return theGraph->functions.fpSortVertices(theGraph); -} - -int _SortVertices(graphP theGraph) -{ -int v, EsizeOccupied, e, srcPos, dstPos; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - if (theGraph == NULL) return NOTOK; - if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED)) - if (gp_CreateDFSTree(theGraph) != OK) - return NOTOK; - - gp_LogLine("\ngraphDFSUtils.c/_SortVertices() start"); - - /* Change labels of edges from v to DFI(v)-- or vice versa - Also, if any links go back to locations 0 to n-1, then they - need to be changed because we are reordering the vertices */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2) - { - if (gp_EdgeInUse(theGraph, e)) - { - gp_SetNeighbor(theGraph, e, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e))); - gp_SetNeighbor(theGraph, e+1, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e+1))); - } - } - - /* Convert DFSParent from v to DFI(v) or vice versa */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - if (gp_IsNotDFSTreeRoot(theGraph, v)) - gp_SetVertexParent(theGraph, v, gp_GetVertexIndex(theGraph, gp_GetVertexParent(theGraph, v))); - - /* Sort by 'v using constant time random access. Move each vertex to its - destination 'v', and store its source location in 'v'. */ - - /* First we clear the visitation flags. We need these to help mark - visited vertices because we change the 'v' field to be the source - location, so we cannot use index==v as a test for whether the - correct vertex is in location 'index'. */ - - _ClearVertexVisitedFlags(theGraph, FALSE); - - /* We visit each vertex location, skipping those marked as visited since - we've already moved the correct vertex into that location. The - inner loop swaps the vertex at location v into the correct position, - given by the index of the vertex at location v. Then it marks that - location as visited, then sets its index to be the location from - whence we obtained the vertex record. */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - srcPos = v; - while (!gp_GetVertexVisited(theGraph, v)) - { - dstPos = gp_GetVertexIndex(theGraph, v); - - gp_SwapVertexRec(theGraph, dstPos, theGraph, v); - gp_SwapVertexInfo(theGraph, dstPos, theGraph, v); - - gp_SetVertexVisited(theGraph, dstPos); - gp_SetVertexIndex(theGraph, dstPos, srcPos); - - srcPos = dstPos; - } - } - - /* Invert the bit that records the sort order of the graph */ - - theGraph->internalFlags ^= FLAGS_SORTEDBYDFI; - - gp_LogLine("graphDFSUtils.c/_SortVertices() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("SortVertices in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - gp_LowpointAndLeastAncestor() - leastAncestor(v): min(v, ancestor neighbors of v, excluding parent) - Lowpoint(v): min(leastAncestor(v), Lowpoint of DFS children of v) - - Lowpoint is computed via a post-order traversal of the DFS tree. - We push the root of the DFS tree, then we loop while the stack is not empty. - We pop a vertex; if it is not marked, then we are on our way down the DFS - tree, so we mark it and push it back on, followed by pushing its - DFS children. The next time we pop the node, all of its children - will have been popped, marked+children pushed, and popped again. On - the second pop of the vertex, we can therefore compute the lowpoint - values based on the childrens' lowpoints and the least ancestor from - among the edges in the vertex's adjacency list. - - If they have not already been performed, gp_CreateDFSTree() and - gp_SortVertices() are invoked on the graph, and it is left in the - sorted state on completion of this method. - - NOTE: This is a utility function provided for general use of the graph - library. The core planarity algorithm computes leastAncestor during its - initial DFS, and it computes the lowpoint of a vertex as it embeds the - tree edges to its children. - ********************************************************************/ - -int gp_LowpointAndLeastAncestor(graphP theGraph) -{ -stackP theStack = theGraph->theStack; -int v, u, uneighbor, e, L, leastAncestor; - - if (theGraph == NULL) return NOTOK; - - if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED)) - if (gp_CreateDFSTree(theGraph) != OK) - return NOTOK; - - if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI)) - if (gp_SortVertices(theGraph) != OK) - return NOTOK; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - gp_LogLine("\ngraphDFSUtils.c/gp_LowpointAndLeastAncestor() start"); - - // A stack of size N suffices because at maximum every vertex is pushed only once - // However, since a larger stack is needed for the main DFS, this is mainly documentation - if (sp_GetCapacity(theStack) < theGraph->N) - return NOTOK; - - sp_ClearStack(theStack); - - _ClearVertexVisitedFlags(theGraph, FALSE); - - // This outer loop causes the connected subgraphs of a disconnected graph to be processed - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);) - { - if (gp_GetVertexVisited(theGraph, v)) - { - ++v; - continue; - } - - sp_Push(theStack, v); - while (sp_NonEmpty(theStack)) - { - sp_Pop(theStack, u); - - // If not visited, then we're on the pre-order visitation, so push u and its DFS children - if (!gp_GetVertexVisited(theGraph, u)) - { - // Mark u as visited, then push it back on the stack - gp_SetVertexVisited(theGraph, u); - ++v; - sp_Push(theStack, u); - - // Push the DFS children of u - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push(theStack, gp_GetNeighbor(theGraph, e)); - } - - e = gp_GetNextArc(theGraph, e); - } - } - - // If u has been visited before, then this is the post-order visitation - else - { - // Start with high values because we are doing a min function - leastAncestor = L = u; - - // Compute leastAncestor and L, the least lowpoint from the DFS children - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - uneighbor = gp_GetNeighbor(theGraph, e); - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - if (L > gp_GetVertexLowpoint(theGraph, uneighbor)) - L = gp_GetVertexLowpoint(theGraph, uneighbor); - } - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) - { - if (leastAncestor > uneighbor) - leastAncestor = uneighbor; - } - - e = gp_GetNextArc(theGraph, e); - } - - /* Assign leastAncestor and Lowpoint to the vertex */ - gp_SetVertexLeastAncestor(theGraph, u, leastAncestor); - gp_SetVertexLowpoint(theGraph, u, leastAncestor < L ? leastAncestor : L); - } - } - } - - gp_LogLine("graphDFSUtils.c/gp_LowpointAndLeastAncestor() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("Lowpoint in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - gp_LeastAncestor() - - By simple pre-order visitation, compute the least ancestor of each - vertex that is directly adjacent to the vertex by a back edge. - - If they have not already been performed, gp_CreateDFSTree() and - gp_SortVertices() are invoked on the graph, and it is left in the - sorted state on completion of this method. - - NOTE: This method is not called by gp_LowpointAndLeastAncestor(), - which computes both values at the same time. - - NOTE: This method is useful in core planarity initialization when - a graph has already been DFS numbered and sorted by DFI. For example, - this allows the core planarity embedder to avoid perturbing unit test - graphs that may be designed and stored in a DFI sorted format. - ********************************************************************/ - -int gp_LeastAncestor(graphP theGraph) -{ -stackP theStack = theGraph->theStack; -int v, u, uneighbor, e, leastAncestor; - - if (theGraph == NULL) return NOTOK; - - if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED)) - if (gp_CreateDFSTree(theGraph) != OK) - return NOTOK; - - if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI)) - if (gp_SortVertices(theGraph) != OK) - return NOTOK; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - gp_LogLine("\ngraphDFSUtils.c/gp_LeastAncestor() start"); - - // A stack of size N suffices because at maximum every vertex is pushed only once - if (sp_GetCapacity(theStack) < theGraph->N) - return NOTOK; - - sp_ClearStack(theStack); - - // This outer loop causes the connected subgraphs of a disconnected graph to be processed - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);) - { - if (gp_GetVertexVisited(theGraph, v)) - { - ++v; - continue; - } - - sp_Push(theStack, v); - while (sp_NonEmpty(theStack)) - { - sp_Pop(theStack, u); - - if (!gp_GetVertexVisited(theGraph, u)) - { - gp_SetVertexVisited(theGraph, u); - ++v; - leastAncestor = u; - - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - uneighbor = gp_GetNeighbor(theGraph, e); - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push(theStack, uneighbor); - } - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) - { - if (leastAncestor > uneighbor) - leastAncestor = uneighbor; - } - - e = gp_GetNextArc(theGraph, e); - } - gp_SetVertexLeastAncestor(theGraph, u, leastAncestor); - } - } - } - - gp_LogLine("graphDFSUtils.c/gp_LeastAncestor() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("LeastAncestor in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} diff --git a/planarity_src/c/graphDrawPlanar.h b/planarity_src/c/graphDrawPlanar.h deleted file mode 100644 index 53d0c2e..0000000 --- a/planarity_src/c/graphDrawPlanar.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef GRAPH_DRAWPLANAR_H -#define GRAPH_DRAWPLANAR_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graphStructures.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define DRAWPLANAR_NAME "DrawPlanar" - -int gp_AttachDrawPlanar(graphP theGraph); -int gp_DetachDrawPlanar(graphP theGraph); - -int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/planarity_src/c/graphDrawPlanar.private.h b/planarity_src/c/graphDrawPlanar.private.h deleted file mode 100644 index 42b03b5..0000000 --- a/planarity_src/c/graphDrawPlanar.private.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef GRAPH_DRAWPLANAR_PRIVATE_H -#define GRAPH_DRAWPLANAR_PRIVATE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 - diff --git a/planarity_src/c/graphEmbed.c b/planarity_src/c/graphEmbed.c deleted file mode 100644 index 7df06af..0000000 --- a/planarity_src/c/graphEmbed.c +++ /dev/null @@ -1,1404 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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; -} diff --git a/planarity_src/c/graphExtensions.h b/planarity_src/c/graphExtensions.h deleted file mode 100644 index adca39c..0000000 --- a/planarity_src/c/graphExtensions.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef GRAPH_EXTENSIONS_H -#define GRAPH_EXTENSIONS_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 diff --git a/planarity_src/c/graphExtensions.private.h b/planarity_src/c/graphExtensions.private.h deleted file mode 100644 index 955751d..0000000 --- a/planarity_src/c/graphExtensions.private.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef GRAPH_EXTENSIONS_PRIVATE_H -#define GRAPH_EXTENSIONS_PRIVATE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 diff --git a/planarity_src/c/graphFunctionTable.h b/planarity_src/c/graphFunctionTable.h deleted file mode 100644 index 150d440..0000000 --- a/planarity_src/c/graphFunctionTable.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef GRAPHFUNCTIONTABLE_H -#define GRAPHFUNCTIONTABLE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 diff --git a/planarity_src/c/graphIO.c b/planarity_src/c/graphIO.c deleted file mode 100644 index 4d42d0e..0000000 --- a/planarity_src/c/graphIO.c +++ /dev/null @@ -1,797 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include -#include - -#include "graph.h" - -/* Private functions (exported to system) */ - -int _ReadAdjMatrix(graphP theGraph, FILE *Infile); -int _ReadAdjList(graphP theGraph, FILE *Infile); -int _WriteAdjList(graphP theGraph, FILE *Outfile); -int _WriteAdjMatrix(graphP theGraph, FILE *Outfile); -int _WriteDebugInfo(graphP theGraph, FILE *Outfile); - -/******************************************************************** - _ReadAdjMatrix() - This function reads the undirected graph in upper triangular matrix format. - Though O(N^2) time is required, this routine is useful during - reliability testing due to the wealth of graph generating software - that uses this format for output. - Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges - ********************************************************************/ - -int _ReadAdjMatrix(graphP theGraph, FILE *Infile) -{ - int N, v, w, Flag; - - if (Infile == NULL) return NOTOK; - fscanf(Infile, " %d ", &N); - if (gp_InitGraph(theGraph, N) != OK) - return NOTOK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - gp_SetVertexIndex(theGraph, v, v); - for (w = v+1; gp_VertexInRange(theGraph, w); w++) - { - fscanf(Infile, " %1d", &Flag); - if (Flag) - { - if (gp_AddEdge(theGraph, v, 0, w, 0) != OK) - return NOTOK; - } - } - } - - return OK; -} - -/******************************************************************** - _ReadAdjList() - This function reads the graph in adjacency list format. - - The file format is - On the first line : N= number of vertices - On N subsequent lines: #: a b c ... -1 - where # is a vertex number and a, b, c, ... are its neighbors. - - NOTE: The vertex number is for file documentation only. It is an - error if the vertices are not in sorted order in the file. - - NOTE: If a loop edge is found, it is ignored without error. - - NOTE: This routine supports digraphs. For a directed arc (v -> W), - an edge record is created in both vertices, v and W, and the - edge record in v's adjacency list is marked OUTONLY while the - edge record in W's list is marked INONLY. - This makes it easy to used edge directedness when appropriate - but also seamlessly process the corresponding undirected graph. - - Returns: OK on success, NONEMBEDDABLE if success except too many edges - NOTOK on file content error (or internal error) - ********************************************************************/ - -int _ReadAdjList(graphP theGraph, FILE *Infile) -{ - int N, v, W, adjList, e, indexValue, ErrorCode; - int zeroBased = FALSE; - - if (Infile == NULL) return NOTOK; - fgetc(Infile); /* Skip the N= */ - fgetc(Infile); - fscanf(Infile, " %d ", &N); /* Read N */ - if (gp_InitGraph(theGraph, N) != OK) - { - printf("Failed to init graph"); - return NOTOK; - } - - // Clear the visited members of the vertices so they can be used - // during the adjacency list read operation - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_SetVertexVisitedInfo(theGraph, v, NIL); - - // Do the adjacency list read operation for each vertex in order - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - // Read the vertex number - fscanf(Infile, "%d", &indexValue); - - if (indexValue == 0 && v == gp_GetFirstVertex(theGraph)) - zeroBased = TRUE; - indexValue += zeroBased ? gp_GetFirstVertex(theGraph) : 0; - - gp_SetVertexIndex(theGraph, v, indexValue); - - // The vertices are expected to be in numeric ascending order - if (gp_GetVertexIndex(theGraph, v) != v) - return NOTOK; - - // Skip the colon after the vertex number - fgetc(Infile); - - // If the vertex already has a non-empty adjacency list, then it is - // the result of adding edges during processing of preceding vertices. - // The list is removed from the current vertex v and saved for use - // during the read operation for v. Adjacencies to preceding vertices - // are pulled from this list, if present, or added as directed edges - // if not. Adjacencies to succeeding vertices are added as undirected - // edges, and will be corrected later if the succeeding vertex does not - // have the matching adjacency using the following mechanism. After the - // read operation for a vertex v, any adjacency nodes left in the saved - // list are converted to directed edges from the preceding vertex to v. - adjList = gp_GetFirstArc(theGraph, v); - if (gp_IsArc(adjList)) - { - // Store the adjacency node location in the visited member of each - // of the preceding vertices to which v is adjacent so that we can - // efficiently detect the adjacency during the read operation and - // efficiently find the adjacency node. - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), e); - e = gp_GetNextArc(theGraph, e); - } - - // Make the adjacency list circular, for later ease of processing - gp_SetPrevArc(theGraph, adjList, gp_GetLastArc(theGraph, v)); - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), adjList); - - // Remove the list from the vertex - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - } - - // Read the adjacency list. - while (1) - { - // Read the value indicating the next adjacent vertex (or the list end) - fscanf(Infile, " %d ", &W); - W += zeroBased ? gp_GetFirstVertex(theGraph) : 0; - - // A value below the valid range indicates the adjacency list end - if (W < gp_GetFirstVertex(theGraph)) - break; - - // A value above the valid range is an error - if (W > gp_GetLastVertex(theGraph)) - return NOTOK; - - // Loop edges are not supported - else if (W == v) - return NOTOK; - - // If the adjacency is to a succeeding, higher numbered vertex, - // then we'll add an undirected edge for now - else if (v < W) - { - if ((ErrorCode = gp_AddEdge(theGraph, v, 0, W, 0)) != OK) - return ErrorCode; - } - - // If the adjacency is to a preceding, lower numbered vertex, then - // we have to pull the adjacency node from the preexisting adjList, - // if it is there, and if not then we have to add a directed edge. - else - { - // If the adjacency node (arc) already exists, then we add it - // as the new first arc of the vertex and delete it from adjList - if (gp_IsArc(gp_GetVertexVisitedInfo(theGraph, W))) - { - e = gp_GetVertexVisitedInfo(theGraph, W); - - // Remove the arc e from the adjList construct - gp_SetVertexVisitedInfo(theGraph, W, NIL); - if (adjList == e) - { - if ((adjList = gp_GetNextArc(theGraph, e)) == e) - adjList = NIL; - } - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e)); - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e)); - - gp_AttachFirstArc(theGraph, v, e); - } - - // If an adjacency node to the lower numbered vertex W does not - // already exist, then we make a new directed arc from the current - // vertex v to W. - else - { - // It is added as the new first arc in both vertices - if ((ErrorCode = gp_AddEdge(theGraph, v, 0, W, 0)) != OK) - return ErrorCode; - - // Note that this call also sets OUTONLY on the twin arc - gp_SetDirection(theGraph, gp_GetFirstArc(theGraph, W), EDGEFLAG_DIRECTION_INONLY); - } - } - } - - // If there are still adjList entries after the read operation - // then those entries are not representative of full undirected edges. - // Rather, they represent incoming directed arcs from other vertices - // into vertex v. They need to be added back into v's adjacency list but - // marked as "INONLY", while the twin is marked "OUTONLY" (by the same function). - while (gp_IsArc(adjList)) - { - e = adjList; - - gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), NIL); - - if ((adjList = gp_GetNextArc(theGraph, e)) == e) - adjList = NIL; - - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e)); - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e)); - - gp_AttachFirstArc(theGraph, v, e); - gp_SetDirection(theGraph, e, EDGEFLAG_DIRECTION_INONLY); - } - } - - if (zeroBased) - theGraph->internalFlags |= FLAGS_ZEROBASEDIO; - - return OK; -} - -/******************************************************************** - _ReadLEDAGraph() - Reads the edge list from a LEDA file containing a simple undirected graph. - LEDA files use a one-based numbering system, which is converted to - zero-based numbers if the graph reports starting at zero as the first vertex. - - Returns: OK on success, NONEMBEDDABLE if success except too many edges - NOTOK on file content error (or internal error) - ********************************************************************/ - -int _ReadLEDAGraph(graphP theGraph, FILE *Infile) -{ - char Line[256]; - int N, M, m, u, v, ErrorCode; - int zeroBasedOffset = gp_GetFirstVertex(theGraph)==(0) ? 1 : 0; - - /* Skip the lines that say LEDA.GRAPH and give the node and edge types */ - fgets(Line, 255, Infile); - fgets(Line, 255, Infile); - fgets(Line, 255, Infile); - - /* Read the number of vertices N, initialize the graph, then skip N. */ - fgets(Line, 255, Infile); - sscanf(Line, " %d", &N); - - if (gp_InitGraph(theGraph, N) != OK) - return NOTOK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - fgets(Line, 255, Infile); - - /* Read the number of edges */ - fgets(Line, 255, Infile); - sscanf(Line, " %d", &M); - - /* Read and add each edge, omitting loops and parallel edges */ - for (m = 0; m < M; m++) - { - fgets(Line, 255, Infile); - sscanf(Line, " %d %d", &u, &v); - if (u != v && !gp_IsNeighbor(theGraph, u-zeroBasedOffset, v-zeroBasedOffset)) - { - if ((ErrorCode = gp_AddEdge(theGraph, u-zeroBasedOffset, 0, v-zeroBasedOffset, 0)) != OK) - return ErrorCode; - } - } - - if (zeroBasedOffset) - theGraph->internalFlags |= FLAGS_ZEROBASEDIO; - - return OK; -} - -/******************************************************************** - gp_Read() - Opens the given file, determines whether it is in adjacency list or - matrix format based on whether the file start with N or just a number, - calls the appropriate read function, then closes the file and returns - the graph. - - Digraphs and loop edges are not supported in the adjacency matrix format, - which is upper triangular. - - In the adjacency list format, digraphs are supported. Loop edges are - ignored without producing an error. - - Pass "stdin" for the FileName to read from the stdin stream - - Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges - ********************************************************************/ - -int gp_Read(graphP theGraph, char *FileName) -{ -FILE *Infile; -char Ch; -int RetVal; - - if (strcmp(FileName, "stdin") == 0) - Infile = stdin; - else if ((Infile = fopen(FileName, READTEXT)) == NULL) - return NOTOK; - - Ch = (char) fgetc(Infile); - ungetc(Ch, Infile); - if (Ch == 'N') - RetVal = _ReadAdjList(theGraph, Infile); - else if (Ch == 'L') - RetVal = _ReadLEDAGraph(theGraph, Infile); - else RetVal = _ReadAdjMatrix(theGraph, Infile); - - if (RetVal == OK) - { - void *extraData = NULL; - long filePos = ftell(Infile); - long fileSize; - - fseek(Infile, 0, SEEK_END); - fileSize = ftell(Infile); - fseek(Infile, filePos, SEEK_SET); - - if (filePos < fileSize) - { - extraData = malloc(fileSize - filePos + 1); - fread(extraData, fileSize - filePos, 1, Infile); - } -/*// Useful for quick debugging of IO extensibility - if (extraData == NULL) - printf("extraData == NULL\n"); - else - { - char *extraDataString = (char *) extraData; - extraDataString[fileSize - filePos] = '\0'; - printf("extraData = '%s'\n", extraDataString); - } -*/ - - if (extraData != NULL) - { - RetVal = theGraph->functions.fpReadPostprocess(theGraph, extraData, fileSize - filePos); - free((void *) extraData); - } - } - - if (strcmp(FileName, "stdin") != 0) - fclose(Infile); - - return RetVal; -} - -int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize) -{ - return OK; -} - -/******************************************************************** - _WriteAdjList() - For each vertex, we write its number, a colon, the list of adjacent vertices, - then a NIL. The vertices occupy the first N positions of theGraph. Each - vertex is also has indicators of the first and last adjacency nodes (arcs) - in its adjacency list. - - Returns: NOTOK if either param is NULL; OK otherwise (after printing - adjacency list representation to Outfile). - ********************************************************************/ - -int _WriteAdjList(graphP theGraph, FILE *Outfile) -{ - int v, e; - int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; - - if (theGraph==NULL || Outfile==NULL) return NOTOK; - - fprintf(Outfile, "N=%d\n", theGraph->N); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - fprintf(Outfile, "%d:", v - zeroBasedOffset); - - e = gp_GetLastArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) - fprintf(Outfile, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset); - - e = gp_GetPrevArc(theGraph, e); - } - - // Write NIL at the end of the adjacency list (in zero-based I/O, NIL was -1) - fprintf(Outfile, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL); - } - return OK; -} - -/******************************************************************** - _WriteAdjMatrix() - Outputs upper triangular matrix representation capable of being - read by _ReadAdjMatrix() - - Note: This routine does not support digraphs and will return an - error if a directed edge is found. - - returns OK for success, NOTOK for failure - ********************************************************************/ - -int _WriteAdjMatrix(graphP theGraph, FILE *Outfile) -{ -int v, e, K; -char *Row = NULL; - - if (theGraph != NULL) - Row = (char *) malloc((theGraph->N+1)*sizeof(char)); - - if (Row==NULL || theGraph==NULL || Outfile==NULL) - { - if (Row != NULL) free(Row); - return NOTOK; - } - - fprintf(Outfile, "%d\n", theGraph->N); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - for (K = gp_GetFirstVertex(theGraph); K <= v; K++) - Row[K - gp_GetFirstVertex(theGraph)] = ' '; - for (K = v+1; gp_VertexInRange(theGraph, v); K++) - Row[K - gp_GetFirstVertex(theGraph)] = '0'; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) == EDGEFLAG_DIRECTION_INONLY) - return NOTOK; - - if (gp_GetNeighbor(theGraph, e) > v) - Row[gp_GetNeighbor(theGraph, e) - gp_GetFirstVertex(theGraph)] = '1'; - - e = gp_GetNextArc(theGraph, e); - } - - Row[theGraph->N] = '\0'; - fprintf(Outfile, "%s\n", Row); - } - - free(Row); - return OK; -} - -/******************************************************************** - ********************************************************************/ - -char _GetEdgeTypeChar(graphP theGraph, int e) -{ - char type = 'U'; - - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - type = 'C'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_FORWARD) - type = 'F'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - type = 'P'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) - type = 'B'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_RANDOMTREE) - type = 'T'; - - return type; -} - -/******************************************************************** - ********************************************************************/ - -char _GetVertexObstructionTypeChar(graphP theGraph, int v) -{ - char type = 'U'; - - if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW) - type = 'X'; - else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - type = 'x'; - if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW) - type = 'Y'; - else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - type = 'y'; - - return type; -} - -/******************************************************************** - _WriteDebugInfo() - Writes adjacency list, but also includes the type value of each - edge (e.g. is it DFS child arc, forward arc or back arc?), and - the L, A and DFSParent of each vertex. - ********************************************************************/ - -int _WriteDebugInfo(graphP theGraph, FILE *Outfile) -{ -int v, e, EsizeOccupied; - - if (theGraph==NULL || Outfile==NULL) return NOTOK; - - /* Print parent copy vertices and their adjacency lists */ - - fprintf(Outfile, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - fprintf(Outfile, "%d(P=%d,lA=%d,LowPt=%d,v=%d):", - v, gp_GetVertexParent(theGraph, v), - gp_GetVertexLeastAncestor(theGraph, v), - gp_GetVertexLowpoint(theGraph, v), - gp_GetVertexIndex(theGraph, v)); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - fprintf(Outfile, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e); - e = gp_GetNextArc(theGraph, e); - } - - fprintf(Outfile, " %d\n", NIL); - } - - /* Print any root copy vertices and their adjacency lists */ - - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - { - if (!gp_VirtualVertexInUse(theGraph, v)) - continue; - - fprintf(Outfile, "%d(copy of=%d, DFS child=%d):", - v, gp_GetVertexIndex(theGraph, v), - gp_GetDFSChildFromRoot(theGraph, v)); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - fprintf(Outfile, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e); - e = gp_GetNextArc(theGraph, e); - } - - fprintf(Outfile, " %d\n", NIL); - } - - /* Print information about vertices and root copy (virtual) vertices */ - fprintf(Outfile, "\nVERTEX INFORMATION\n"); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - fprintf(Outfile, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n", - v, - gp_GetVertexIndex(theGraph, v), - (gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)), - gp_GetFirstArc(theGraph, v), - gp_GetLastArc(theGraph, v)); - } - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - { - if (gp_VirtualVertexNotInUse(theGraph, v)) - continue; - - fprintf(Outfile, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n", - v, - gp_GetVertexIndex(theGraph, v), - (gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)), - gp_GetFirstArc(theGraph, v), - gp_GetLastArc(theGraph, v)); - } - - /* Print information about edges */ - - fprintf(Outfile, "\nEDGE INFORMATION\n"); - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - { - if (gp_EdgeInUse(theGraph, e)) - { - fprintf(Outfile, "E[%3d] neighbor=%3d, type=%c, next arc=%3d, prev arc=%3d\n", - e, - gp_GetNeighbor(theGraph, e), - _GetEdgeTypeChar(theGraph, e), - gp_GetNextArc(theGraph, e), - gp_GetPrevArc(theGraph, e)); - } - } - - return OK; -} - -/******************************************************************** - gp_Write() - Writes theGraph into the file. - Pass "stdout" or "stderr" to FileName to write to the corresponding stream - Pass WRITE_ADJLIST, WRITE_ADJMATRIX or WRITE_DEBUGINFO for the Mode - - NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST - - Returns NOTOK on error, OK on success. - ********************************************************************/ - -int gp_Write(graphP theGraph, char *FileName, int Mode) -{ -FILE *Outfile; -int RetVal; - - if (theGraph == NULL || FileName == NULL) - return NOTOK; - - if (strcmp(FileName, "nullwrite") == 0) - return OK; - - if (strcmp(FileName, "stdout") == 0) - Outfile = stdout; - else if (strcmp(FileName, "stderr") == 0) - Outfile = stderr; - else if ((Outfile = fopen(FileName, WRITETEXT)) == NULL) - return NOTOK; - - switch (Mode) - { - case WRITE_ADJLIST : - RetVal = _WriteAdjList(theGraph, Outfile); - break; - case WRITE_ADJMATRIX : - RetVal = _WriteAdjMatrix(theGraph, Outfile); - break; - case WRITE_DEBUGINFO : - RetVal = _WriteDebugInfo(theGraph, Outfile); - break; - default : - RetVal = NOTOK; - break; - } - - if (RetVal == OK) - { - void *extraData = NULL; - long extraDataSize; - - RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData, &extraDataSize); - - if (extraData != NULL) - { - if (!fwrite(extraData, extraDataSize, 1, Outfile)) - RetVal = NOTOK; - free(extraData); - } - } - - if (strcmp(FileName, "stdout") == 0 || strcmp(FileName, "stderr") == 0) - fflush(Outfile); - - else if (fclose(Outfile) != 0) - RetVal = NOTOK; - - return RetVal; -} - -/******************************************************************** - _WritePostprocess() - - By default, no additional information is written. - ********************************************************************/ - -int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize) -{ - return OK; -} - -/******************************************************************** - _Log() - - When the project is compiled with LOGGING enabled, this method writes - a string to the file PLANARITY.LOG in the current working directory. - On first write, the file is created or cleared. - Call this method with NULL to close the log file. - ********************************************************************/ - -void _Log(char *Str) -{ -static FILE *logfile = NULL; - - if (logfile == NULL) - { - if ((logfile = fopen("PLANARITY.LOG", WRITETEXT)) == NULL) - return; - } - - if (Str != NULL) - { - fprintf(logfile, "%s", Str); - fflush(logfile); - } - else - fclose(logfile); -} - -void _LogLine(char *Str) -{ - _Log(Str); - _Log("\n"); -} - -static char LogStr[512]; - -char *_MakeLogStr1(char *format, int one) -{ - sprintf(LogStr, format, one); - return LogStr; -} - -char *_MakeLogStr2(char *format, int one, int two) -{ - sprintf(LogStr, format, one, two); - return LogStr; -} - -char *_MakeLogStr3(char *format, int one, int two, int three) -{ - sprintf(LogStr, format, one, two, three); - return LogStr; -} - -char *_MakeLogStr4(char *format, int one, int two, int three, int four) -{ - sprintf(LogStr, format, one, two, three, four); - return LogStr; -} - -char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five) -{ - sprintf(LogStr, format, one, two, three, four, five); - return LogStr; -} diff --git a/planarity_src/c/graphIsolator.c b/planarity_src/c/graphIsolator.c deleted file mode 100644 index 5b7ad92..0000000 --- a/planarity_src/c/graphIsolator.c +++ /dev/null @@ -1,868 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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; -} diff --git a/planarity_src/c/graphK23Search.c b/planarity_src/c/graphK23Search.c deleted file mode 100644 index 431abc6..0000000 --- a/planarity_src/c/graphK23Search.c +++ /dev/null @@ -1,308 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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); -} diff --git a/planarity_src/c/graphK23Search.h b/planarity_src/c/graphK23Search.h deleted file mode 100644 index 04a36ab..0000000 --- a/planarity_src/c/graphK23Search.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef GRAPH_K23SEARCH_H -#define GRAPH_K23SEARCH_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 - diff --git a/planarity_src/c/graphK23Search.private.h b/planarity_src/c/graphK23Search.private.h deleted file mode 100644 index 03a6971..0000000 --- a/planarity_src/c/graphK23Search.private.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef GRAPH_K23SEARCH_PRIVATE_H -#define GRAPH_K23SEARCH_PRIVATE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - // Overloaded function pointers - graphFunctionTable functions; - -} K23SearchContext; - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/planarity_src/c/graphK23Search_Extensions.c b/planarity_src/c/graphK23Search_Extensions.c deleted file mode 100644 index 8bb0f59..0000000 --- a/planarity_src/c/graphK23Search_Extensions.c +++ /dev/null @@ -1,291 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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; -} diff --git a/planarity_src/c/graphK33Search.c b/planarity_src/c/graphK33Search.c deleted file mode 100644 index 57ad778..0000000 --- a/planarity_src/c/graphK33Search.c +++ /dev/null @@ -1,2092 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graphK33Search.h" -#include "graphK33Search.private.h" - -//extern int K33SEARCH_ID; - -#include "graph.h" - -/* Imported functions */ - -//extern void _ClearVisitedFlags(graphP); -extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); -extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); -extern int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue); - -//extern int _GetBicompSize(graphP theGraph, int BicompRoot); -extern int _HideInternalEdges(graphP theGraph, int vertex); -extern int _RestoreInternalEdges(graphP theGraph, int stackBottom); -extern int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot); -extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); -extern int _SetEdgeType(graphP theGraph, int u, int v); - -extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -extern int _JoinBicomps(graphP theGraph); -extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); -extern int _OrientVerticesInEmbedding(graphP theGraph); -//extern void _InvertVertex(graphP theGraph, int V); -extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); -extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); -extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); - -extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); -extern int _MarkHighestXYPath(graphP theGraph); - -extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); - -extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); -extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); -extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); - -extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); - -extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); - -extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -extern int _IsolateMinorE1(graphP theGraph); -//extern int _IsolateMinorE2(graphP theGraph); -extern int _IsolateMinorE3(graphP theGraph); -extern int _IsolateMinorE4(graphP theGraph); - -extern int _MarkDFSPathsToDescendants(graphP theGraph); -extern int _AddAndMarkUnembeddedEdges(graphP theGraph); - -extern void _K33Search_InitEdgeRec(K33SearchContext *context, int e); - -/* Private functions for K_{3,3} searching. */ - -int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R); - -int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context); -int _SearchForMinorE1(graphP theGraph); -int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context); -int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max); -int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex); -int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int vertex, - int closerAncestor, int fartherAncestor); -int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, - int cutVertex, int *pDescendant); -int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker); -int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker); - -int _TestForLowXYPath(graphP theGraph); -int _TestForZtoWPath(graphP theGraph); -int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max); -int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot); -int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink); -int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R); -int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType); -int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType); -int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e); -int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context); - -int _IsolateMinorE5(graphP theGraph); -int _IsolateMinorE6(graphP theGraph, K33SearchContext *context); -int _IsolateMinorE7(graphP theGraph, K33SearchContext *context); - -/**************************************************************************** - _SearchForK33InBicomp() - ****************************************************************************/ - -int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R) -{ -isolatorContextP IC = &theGraph->IC; -int tempResult; - -/* Begin by determining which non-planarity minor is detected */ - - if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - -/* If minor A is selected, then the root of the oriented bicomp has been changed */ - - else R = IC->r; - -/* Minors A to D result in the desired K_{3,3} homeomorph, - so we isolate it and return NONEMBEDDABLE. */ - - if (theGraph->IC.minorType & (MINORTYPE_A|MINORTYPE_B|MINORTYPE_C|MINORTYPE_D)) - { - /* First we restore the orientations of the vertices in the - one bicomp we have messed with so that there is no confusion. */ - - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - /* Next we restore the orientation of the embedding so we - can restore the reduced paths (because we avoid modifying - the Kuratowski subgraph isolator to restore reduced paths, - which are a construct of the K_{3,3} search). */ - - if (_OrientVerticesInEmbedding(theGraph) != OK || - _RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - /* Next we simply call the Kuratowski subgraph isolation since - we know now that it will isolate a K_{3,3}. - For minor A, we need to set up the stack that would be - available immediately after a Walkdown failure. */ - - if (theGraph->IC.minorType & MINORTYPE_A) - { - sp_ClearStack(theGraph->theStack); - sp_Push2(theGraph->theStack, R, NIL); - } - - if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* For minor E (a K5 minor), we run the additional tests to see if minors E1 to E4 apply - since these minors isolate a K_{3,3} entangled with the K5. - This is the key location where GetLeastAncestorConnection() must be constant time. */ - - IC->ux = _Fast_GetLeastAncestorConnection(theGraph, context, IC->x); - IC->uy = _Fast_GetLeastAncestorConnection(theGraph, context, IC->y); - IC->uz = _Fast_GetLeastAncestorConnection(theGraph, context, IC->z); - - if (IC->z != IC->w || - IC->uz > MAX(IC->ux, IC->uy) || - (IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) || - (IC->x != IC->px || IC->y != IC->py)) - { - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - if (_OrientVerticesInEmbedding(theGraph) != OK || - _RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* If the Kuratowski subgraph isolator will not isolate a K_{3,3} based on minor E, - then a K5 homeomorph could be isolated. However, a K_{3,3} may still be tangled - with the K5, so we now run the additional tests of the K_{3,3} search algorithm. - - If the search finds a K_{3,3} (tempResult of NONEMBEDDABLE), then we remove unwanted - edges from the graph and return NONEMBEDDABLE. If the search has a fault (NOTOK), - then we return. If the result is OK, then a K_{3,3} was not found at this time - and we proceed with some clean-up work below. */ - - if ((tempResult = _RunExtraK33Tests(theGraph, context)) != OK) - { - if (tempResult == NONEMBEDDABLE) - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return tempResult; - } - -/* The extra cases for finding a K_{3,3} did not succeed, so the bicomp rooted by R - is either a K5 homeomorph (with at most a superficially entangled K_{3,3}) or - we have made the special setting that allows us to detect the one "merge blocker" - case that would be too costly to try now. Either way, we can safely reduce the - bicomp to the 4-cycle (R, X, W, Y, R) and proceed with the planarity algorithm. - We also restore the mixed orientation of the bicomp (i.e. the proper - orientation in the context of the edge signs) because this code can work - when ReduceBicomp doesn't do any actual work. */ - - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - if (_ReduceBicomp(theGraph, context, R) != OK) - return NOTOK; - -/* Set visitedInfo values in the bicomp to the initialized state so the planarity - algorithm can properly do the Walkup procedure in future steps */ - - if (_FillVertexVisitedInfoInBicomp(theGraph, IC->r, theGraph->N) != OK) - return NOTOK; - -/* We now intend to ignore the pertinence of W (conceptually eliminating - the connection from W to the current vertex). Note that none of the - bicomp roots in the pertinentRootsList (nor their respective subtrees) - will be visited again by the planarity algorithm because they must've - been only pertinent. If they were future pertinent and pertinent, - then we would've found a K_{3,3} by non-planarity minor B. Thus, the original - Walkup costs that identified the pertinent bicomps we intend to ignore are - one-time costs, preserving linear time. */ - - gp_SetVertexPertinentEdge(theGraph, IC->w, NIL); - gp_SetVertexPertinentRootsList(theGraph, IC->w, NIL); - - return OK; -} - -/**************************************************************************** - _RunExtraK33Tests() - ****************************************************************************/ - -#define USE_MERGEBLOCKER - -int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; -int u_max = MAX3(IC->ux, IC->uy, IC->uz); - -#ifndef USE_MERGEBLOCKER -int u; -#endif - -/* Case 1: If there is a pertinent or future pertinent vertex other than W - on the lower external face path between X and Y (the points of - attachment of the x-y path), then we can isolate a K_{3,3} homeomorph - by Minor E1. */ - - if (_SearchForMinorE1(theGraph) != OK) - return NOTOK; - - if (IC->w != IC->z) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE1(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* Case 2: If W/Z can make an external connection to an ancestor of V - that is descendant to u_{max}, then a K_{3,3} homeomorph can - be isolated with Minor E2. - - OPTIMIZATION: We do not need to check for this case. - We avoid doing so because in very specially crafted cases - it could be too costly if the connection doesn't exist. - However, if the highest numbered ancestor H of the current vertex - that has an external connection from W is a descendant u_{max} - then we will discover a K_{3,3} by Minor A or B in step H - (unless some other test succeeds at finding a K_{3,3} first), - so we just let the non-planarity detector do its work since - Minors A and B both provide a K_{3,3} when found. - - This happens because w is pertinent to H and future pertinent - to u_max or an ancestor of u_max. - - Minor A will happen if, in step H, Walkdown descends to the - bicomp containing the current vertex, x, y and w. Since x - and y would still be future pertinent (they connect to u_max - or higher, i.e. with lesser DFI, than u_max). - - Minor B will happen if the bicomp containing the current vertex, - x, y and w is a descendant of a bicomp that blocks planarity - in step H. The bicomp would be both pertinent (due to w's - connection to H) and future pertinent(due to connections to - ancestors of H by w, x and y). -*/ - -#ifndef USE_MERGEBLOCKER - u = _SearchForDescendantExternalConnection(theGraph, context, IC->w, u_max); - if (u > u_max) - { - IC->uz = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE2(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } -#endif - -/* Case 3: If X or Y can make an external connection to an ancestor of V - that is descendant to u_{max}, then a K_{3,3} homeomorph - can be isolated with Minor E3. - - NOTE: Due to the prior use of the Kuratowski subgraph - isolator, we know that at most one of X, Y or W/Z could have an - external connection to an ancestor of u_{max} = MAX(ux, uy, uz). - - OPTIMIZATION: We do not check for the lower connection required - to find Minor E3 because it might ultimately be too costly. - Instead, we mark the vertex with a 'merge blocker' of u_{max}. - If the planar embedder attempts to merge the vertex prior to step - u_{max}, then the embedder has found the desired connection and a - K_{3,3} homeomorph is isolated at that time. -*/ - -#ifdef USE_MERGEBLOCKER - context->VI[IC->x].mergeBlocker = u_max; -#endif -#ifndef USE_MERGEBLOCKER - u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max); - if (u > u_max) - { - IC->ux = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } -#endif - -#ifdef USE_MERGEBLOCKER - context->VI[IC->y].mergeBlocker = u_max; -#endif -#ifndef USE_MERGEBLOCKER - u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max); - if (u > u_max) - { - IC->uy = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } -#endif - -/* Case 4: If there exists any x-y path with points of attachment px and py - such that px!=x or py!=y, then a K_{3,3} homeomorph can be isolated - with Minor E4. */ - - if (_TestForLowXYPath(theGraph) != OK) - return NOTOK; - - if (IC->px != IC->x || IC->py != IC->y) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE4(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* Case 5: If the x-y path contains an internal vertex that starts a second - internal path from the internal vertex to W/Z, then a K_{3,3} homeomorph - can be isolated with Minor E5. */ - - if (_TestForZtoWPath(theGraph) != OK) - return NOTOK; - - if (gp_GetVertexVisited(theGraph, IC->w)) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE5(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* Case 6: If uz < u_{max} and there is an external connection (other than external - connections involving X, Y and W/Z) between an ancestor of u_{max} and a - vertex in the range [V...u_{max}), then a K_{3,3} homeomorph can be - isolated with Minor E6. - - OPTIMIZATION: See _TestForStraddlingBridge() */ - - if (IC->uz < u_max) - { - if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max))) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE6(theGraph, context) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - } - -/* Case 7: If ux < u_{max} or uy < u_{max} and there is an external connection - between an ancestor of u_{max} and a vertex in the range [V...u_{max}) - (except for external connections involving X, Y and W/Z), then a K_{3,3} - homeomorph can be isolated with Minor E7. - - OPTIMIZATION: Same as Case 6.*/ - - if (IC->ux < u_max || IC->uy < u_max) - { - if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max))) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE7(theGraph, context) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - } - -/* If none of the tests found a K_{3,3}, then we return OK to indicate that nothing - went wrong, but a K_{3,3} was not found. */ - - return OK; -} - -/**************************************************************************** - _SearchForMinorE1() - Search along the external face below the x-y path for a vertex Z other - than W that is future pertinent or pertinent. - ****************************************************************************/ - -int _SearchForMinorE1(graphP theGraph) -{ -int Z=theGraph->IC.px, ZPrevLink=1; - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - - while (Z != theGraph->IC.py) - { - if (Z != theGraph->IC.w) - { - gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) - { - theGraph->IC.z = Z; - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); - return OK; - } - else if (PERTINENT(theGraph, Z)) - { - /* Swap the roles of W and Z */ - - theGraph->IC.z = theGraph->IC.w; - theGraph->IC.w = Z; - - /* If the new W (indicated by Z) was on the path (R, X, old W) then - the new Z (the old W, which has no type mark) is on the path - (X, new W, new Z, Y) so we change the type new Z to being on the - RYW path. Otherwise, the order is (X, new Z, new W, Y), so the - new Z (old W with no type) is type changed to be on the RXW path.*/ - - if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RYW); - else gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RXW); - - /* For completeness, we change the new W to type unknown */ - - gp_ClearVertexObstructionType(theGraph, theGraph->IC.w); - - /* The external activity ancestor connection of the new Z must be obtained */ - - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, theGraph->IC.z); - - return OK; - } - } - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - return OK; -} - -/**************************************************************************** - _FinishIsolatorContextInitialization() - Once it has been decided that a desired subgraph can be isolated, it - becomes safe to finish the isolator context initialization. - ****************************************************************************/ - -int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; - -/* Restore the orientation of the bicomp on which we're working, then - perform orientation of all vertices in graph. (An unnecessary but - polite step that simplifies the description of key states of the - data structures). */ - - if (_OrientVerticesInBicomp(theGraph, IC->r, 1) != OK) - return NOTOK; - - if (_OrientVerticesInEmbedding(theGraph) != OK) - return NOTOK; - -/* Restore any paths that were reduced to single edges */ - - if (_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - -/* We assume that the current bicomp has been marked appropriately, - but we must now clear the visitation flags of all other bicomps. */ - - if (_ClearVisitedFlagsInOtherBicomps(theGraph, IC->r) != OK) - return NOTOK; - -/* To complete the normal behavior of _ClearVisitedFlags() in the - normal isolator context initialization, we also have to clear - the visited flags on all edges that have not yet been embedded */ - - _ClearVisitedFlagsInUnembeddedEdges(theGraph); - -/* Now we can find the descendant ends of unembedded back edges based on - the ancestor settings ux, uy and uz. */ - - if (_FindExternalConnectionDescendantEndpoint(theGraph, IC->ux, IC->x, &IC->dx) != OK || - _FindExternalConnectionDescendantEndpoint(theGraph, IC->uy, IC->y, &IC->dy) != OK || - _FindExternalConnectionDescendantEndpoint(theGraph, IC->uz, IC->z, &IC->dz) != OK) - return NOTOK; - -/* Finally, we obtain the descendant end of an unembedded back edge to - the current vertex. */ - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _Fast_GetLeastAncestorConnection() - - This function searches for an ancestor of the current vertex v adjacent by a - cycle edge to the given cutVertex or one of its DFS descendants appearing in - a separated bicomp. The given cutVertex is assumed to be future pertinent - such that either the leastAncestor or the lowpoint of a separated DFS child - is less than v. We obtain the minimum possible connection from the cutVertex - to an ancestor of v. - - This function performs the same operation as _GetLeastAncestorConnection(), - except in constant time. - ****************************************************************************/ - -int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex) -{ - int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); - int child = context->VI[cutVertex].separatedDFSChildList; - - if (gp_IsVertex(child) && ancestor > gp_GetVertexLowpoint(theGraph, child)) - ancestor = gp_GetVertexLowpoint(theGraph, child); - - return ancestor; -} - -/**************************************************************************** - _GetAdjacentAncestorInRange() - Returns the ancestor of theVertex that is adjacent to theVertex by an - unembedded back edge and has a DFI strictly between closerAncestor and - fartherAncestor. - Returns NIL if theVertex has no such neighboring ancestor. - ****************************************************************************/ - -int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int theVertex, - int closerAncestor, int fartherAncestor) -{ -int e = context->VI[theVertex].backArcList; - - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) < closerAncestor && - gp_GetNeighbor(theGraph, e) > fartherAncestor) - return gp_GetNeighbor(theGraph, e); - - e = gp_GetNextArc(theGraph, e); - if (e == context->VI[theVertex].backArcList) - e = NIL; - } - return NIL; -} - -/**************************************************************************** - _SearchForDescendantExternalConnection() - Search the cutVertex and each separated child subtree for an external - connection to a vertex ancestor to the current vertex V and descendant to u_max. - - The function returns the descendant of u_max found to have an external - connection to the given cut vertex. - ****************************************************************************/ - -int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max) -{ -isolatorContextP IC = &theGraph->IC; -int u2 = _GetAdjacentAncestorInRange(theGraph, context, cutVertex, IC->v, u_max); -int child, descendant; - - // Test cutVertex for an external connection to descendant of u_max via direct back edge - if (gp_IsVertex(u2)) - return u2; - - // If there is no direct back edge connection from the cut vertex - // to a vertex on the path between V and u_max, then we will - // look for such a connection in the DFS subtrees rooted by - // separated DFS children of the vertex (ignoring those whose - // lowpoint indicates that they make no external connections) - - // Begin by pushing the separated DFS children of the cut vertex with - // lowpoints indicating connections to ancestors of the current vertex. - sp_ClearStack(theGraph->theStack); - child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex); - while (gp_IsVertex(child)) - { - if (gp_GetVertexLowpoint(theGraph, child) < IC->v && gp_IsSeparatedDFSChild(theGraph, child)) - sp_Push(theGraph->theStack, child); - child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); - } - - // Now process the stack until it is empty or until we've found the desired connection. - while (!sp_IsEmpty(theGraph->theStack)) - { - sp_Pop(theGraph->theStack, descendant); - - // If the vertex has a lowpoint indicating that it makes no external connections, - // then skip the subtree rooted by the vertex - if (gp_GetVertexLowpoint(theGraph, descendant) < IC->v) - { - // Check the subtree root for the desired connection. - u2 = _GetAdjacentAncestorInRange(theGraph, context, descendant, IC->v, u_max); - if (gp_IsVertex(u2)) - return u2; - - // Push each child as a new subtree root to be considered, except skip those whose lowpoint is too great. - child = gp_GetVertexSortedDFSChildList(theGraph, descendant); - while (gp_IsVertex(child)) - { - if (gp_GetVertexLowpoint(theGraph, child) < IC->v) - sp_Push(theGraph->theStack, child); - - child = gp_GetVertexNextDFSChild(theGraph, descendant, child); - } - } - } - - // The only external connections from the cutVertex lead to u_max, so return it. - return u_max; -} - -/**************************************************************************** - _FindExternalConnectionDescendantEndpoint() - - This operation is similar to _FindUnembeddedEdgeToAncestor() except that - we need to be more precise in this case, finding an external connection - from a given cut vertex to a *particular* given ancestor. - - NOTE: By external we don't mean externall active so much as not embedded in - the bicomp containing the cut vertex. - - Returns OK if it finds that either the given cutVertex or one of its - descendants in a separated bicomp has an unembedded back edge - connection to the given ancestor vertex. - Returns NOTOK otherwise (it is an error to not find the descendant because - this function is only called if _SearchForDescendantExternalConnection() - has already determined the existence of the descendant). - ****************************************************************************/ - -int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, - int cutVertex, int *pDescendant) -{ -int child, e; - - // Check whether the cutVertex is directly adjacent to the ancestor - // by an unembedded back edge. - - e = gp_GetVertexFwdArcList(theGraph, ancestor); - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) == cutVertex) - { - *pDescendant = cutVertex; - return OK; - } - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, ancestor)) - e = NIL; - } - - // Now check the descendants of the cut vertex to see if any make - // a connection to the ancestor. - child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex); - while (gp_IsVertex(child)) - { - if (gp_GetVertexLowpoint(theGraph, child) < theGraph->IC.v && gp_IsSeparatedDFSChild(theGraph, child)) - { - if (_FindUnembeddedEdgeToSubtree(theGraph, ancestor, child, pDescendant) == TRUE) - return OK; - } - child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); - } - - return NOTOK; -} - -/**************************************************************************** - _SearchForMergeBlocker() - - This function helps to implement the merge blocking optimization of - _SearchForDescendantExternalConnection(). The function RunExtraK33Tests() - sets a mergeBlocker rather than run _SearchForDescendantExternalConnection() - in certain cases. This procedure is called by MergeBicomps to test the - embedding stack for a merge blocker before merging any biconnected components. - If a merge blocker is found, then FindK33WithMergeBlocker() is called and - ultimately the embedder's Walkdown function is terminated since a K_{3,3} - is isolated. - - Returns OK on success (whether or not the search found a merge blocker) - NOTOK on internal function failure - pMergeBlocker is set to NIL unless a merge blocker is found. - ****************************************************************************/ - -int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker) -{ -stackP tempStack; -int R, Rout, Z, ZPrevLink; - -/* Set return result to 'not found' then return if there is no stack to inspect */ - - *pMergeBlocker = NIL; - - if (sp_IsEmpty(theGraph->theStack)) - return OK; - -/* Create a copy of the embedding stack */ - - tempStack = sp_Duplicate(theGraph->theStack); - if (tempStack == NULL) - return NOTOK; - -/* Search the copy of the embedding stack for a merge blocked vertex */ - - while (!sp_IsEmpty(tempStack)) - { - sp_Pop2(tempStack, R, Rout); - sp_Pop2(tempStack, Z, ZPrevLink); - - if (gp_IsVertex(context->VI[Z].mergeBlocker) && - context->VI[Z].mergeBlocker < v) - { - *pMergeBlocker = Z; - break; - } - } - - sp_Free(&tempStack); - return OK; -} - -/**************************************************************************** - _FindK33WithMergeBlocker() - - This function completes the merge blocking optimization by isolating a K_{3,3} - based on minor E3 if a merge blocked vertex was previously found. - - Returns OK on success, NOTOK on internal function failure - ****************************************************************************/ - -int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker) -{ -int R, RPrevLink, u_max, u, e, W; -isolatorContextP IC = &theGraph->IC; - -/* First, we orient the vertices so we can successfully restore all of the - reduced paths. This needs to be done before reconstructing the context - for CASE 3 of RunExtraK33Tests() because the reconstruction involves - using the Walkup to v from a descendant of v, which will not work if - the descendant is in one of the reduced paths. */ - - if (_OrientVerticesInEmbedding(theGraph) != OK || - _RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - -/* Reconstruct the context that was present for CASE 3 of RunExtraK33Tests() - when we decided to set a mergeBlocker rather than calling - _SearchForDescendantExternalConnection() */ - - /* Obtain the root of the bicomp containing the mergeBlocker. */ - - RPrevLink = 1; - R = mergeBlocker; - while (gp_IsNotVirtualVertex(theGraph, R)) - R = _GetNeighborOnExtFace(theGraph, R, &RPrevLink); - - /* Switch the 'current step' variable v to be equal to the - non-virtual counterpart of the bicomp root. */ - - IC->v = gp_GetPrimaryVertexFromRoot(theGraph, R); - - /* Reinitialize the visitation, pertinence and future pertinence settings from step u_max for step v */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - gp_SetVertexVisitedInfo(theGraph, v, theGraph->N); - gp_SetVertexPertinentEdge(theGraph, v, NIL); - gp_SetVertexPertinentRootsList(theGraph, v, NIL); - - // Any calls to actually determine FUTUREPERTINENT status for a vertex w will actually invoke - // gp_UpdateVertexFuturePertinentChild(theGraph, w, v) beforehand, so only need to reinitialize here - gp_SetVertexFuturePertinentChild(theGraph, v, gp_GetVertexSortedDFSChildList(theGraph, v)); - } - - /* Restore the pertinence settings of step v by doing the Walkup for each - back edge that was not embedded when step v was originally performed. */ - - e = gp_GetVertexFwdArcList(theGraph, IC->v); - while (gp_IsArc(e)) - { - W = gp_GetNeighbor(theGraph, e); - theGraph->functions.fpWalkUp(theGraph, IC->v, e); - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, IC->v)) - e = NIL; - } - -/* Next, we make the standard initialization calls for when we have found - a non-planarity condition. */ - - sp_ClearStack(theGraph->theStack); - - if (_ChooseTypeOfNonplanarityMinor(theGraph, IC->v, R) != OK) - return NOTOK; - - IC->ux = _GetLeastAncestorConnection(theGraph, IC->x); - IC->uy = _GetLeastAncestorConnection(theGraph, IC->y); - IC->uz = _GetLeastAncestorConnection(theGraph, IC->z); - - u_max = MAX3(IC->ux,IC->uy,IC->uz); - -/* Perform the remainder of CASE 3 of RunExtraK33Tests() */ - - if (mergeBlocker == IC->x) - { - u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max); - if (u > u_max) - { - IC->ux = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - } - else return NOTOK; - } - else if (mergeBlocker == IC->y) - { - u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max); - if (u > u_max) - { - IC->uy = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - } - else return NOTOK; - } - else return NOTOK; - -/* Do the final clean-up to obtain the K_{3,3} */ - - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _TestForLowXYPath() - Is there an x-y path that does not include X? - If not, is there an x-y path that does not include Y? - If not, then we restore the original x-y path. - If such a low x-y path exists, then we adjust px or py accordingly, - and we make sure that X or Y (whichever is excluded) and its edges are - not marked visited. - This method uses the stack, though it is called with an empty stack currently, - it does happen to preserve any preceding stack content. This method pushes - at most one integer per edge incident to the bicomp root plus two integers - per vertex in the bicomp. - ****************************************************************************/ - -int _TestForLowXYPath(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; -int result; -int stackBottom; - -/* Clear the previously marked X-Y path */ - - if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) - return NOTOK; - -/* Save the size of the stack before hiding any edges, so we will know - how many edges to restore */ - - stackBottom = sp_GetCurrentSize(theGraph->theStack); - -/* Hide the internal edges of X */ - - if (_HideInternalEdges(theGraph, IC->x) != OK) - return NOTOK; - -/* Try to find a low X-Y path that excludes X, then restore the - internal edges of X. */ - - result = _MarkHighestXYPath(theGraph); - if (_RestoreInternalEdges(theGraph, stackBottom) != OK) - return NOTOK; - -/* If we found the low X-Y path, then return. */ - - if (result == TRUE) - return OK; - -/* Hide the internal edges of Y */ - - if (_HideInternalEdges(theGraph, IC->y) != OK) - return NOTOK; - -/* Try to find a low X-Y path that excludes Y, then restore the - internal edges of Y. */ - - result = _MarkHighestXYPath(theGraph); - if (_RestoreInternalEdges(theGraph, stackBottom) != OK) - return NOTOK; - -/* If we found the low X-Y path, then return. */ - - if (result == TRUE) - return OK; - -/* Restore the original X-Y path and return with no error - (the search failure is reflected by no change to px and py */ - - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _TestForZtoWPath() - This function tests whether there is a path inside the bicomp leading from W - to some internal node of the x-y path. If there is, the path is marked (the - visited flags of its vertices and edges are set). - - Upon function return, the marking (visited flag setting) of W distinguishes - whether the path was found. - - The function returns NOTOK on internal error, OK otherwise. - - Preconditions: All internal vertices have an obstruction type setting of - unknown, as do W and the bicomp root. There is an X-Y path marked visited. - So, we start a depth first search from W to find a visited vertex, except - we prune the search to ignore vertices whose obstruction type is other than - unknown. This ensures the path found, if any, avoids external face vertices, - including avoiding X and Y. Furthermore, the path search is completed without - traversing to R due to the obstructing X-Y path. - - The depth first search has to "mark" the vertices it has seen as visited, - but the visited flags are already in use to distinguish the X-Y path. - So, we reuse the visitedInfo setting of each vertex. The core planarity - algorithm makes settings between 0 and N, so we will regard all of those - as indicating 'unvisited' by this method, and use -1 to indicate visited. - These markings need not be cleaned up because, if the desired path is found - the a K_{3,3} is isolated and if the desired path is not found then the - bicomp is reduced and the visitedInfo in the remaining vertices are set - appropriately for future Walkup processing of the core planarity algorithm. - - For each vertex we visit, if it is an internal vertex on the X-Y path - (i.e. visited flag set and obstruction type unknown), then we want to stop - and unroll the stack to obtain the desired path (described below). If the - vertex is internal but not on the X-Y path (i.e. visited flag clear and - obstruction type unknown), then we want to visit its neighbors, except - those already marked visited by this method (i.e. those with visitedInfo - of -1) and those with a known obstruction type. - - We want to manage the stack so that it when the desired vertex is found, - the stack contains the desired path. So, we do not simply push all the - neighbors of the vertex being visited. First, given that we have popped - some vertex-edge pair (v, e), we push *only* the next edge after e in - v's adjacency list (starting with the first if e is NIL) that leads to a - new 'eligible' vertex. An eligible vertex is one whose obstruction type - is unknown and whose visitedInfo is other than -1 (so, internal and not - yet processed by this method). Second, when we decide a new vertex w - adjacent to v is eligible, we push not only (v, e) but also (w, NIL). - When we later pop the vertex-edge pair containing NIL, we know that - the vertex obstruction type is unknown so we test whether its visited - flag is set (indicating an internal vertex on the X-Y path). If so, then - we can stop the depth first search, then use the vertices and edges - remaining on the stack to mark the desired path from the external face - vertex W to an internal vertex Z on the X-Y path. - - If we pop (v, NIL) and find that the visited flag of v is clear, then it - is not the desired connection endpoint to the X-Y path. We need to process - all paths extending from it, but we don't want any of those paths to cycle - back to this vertex, so we mark it as ineligible by putting -1 in its - visitedInfo member. This is also the case in which the _first_ edge record e - leading from v to an eligible vertex w is obtained, whereupon we push both - (v, e) and (w, NIL). Eventually all paths leading from w to eligible - vertices will be explored, and if none find the desired vertex connection - to the X-Y path, then (v, e) is popped. Now we search the adjacency list of - v starting after e to find the _next_ edge record that indicates the an - eligible vertex to visit. None of the vertices processed while visiting paths - extending from w will be eligible anymore, so it can be seen that this method - is a depth first search. If no remaining edges from v indicate eligible - vertices, then nothing is pushed and we simply go to the next iteration, - which pops a 2-tuple containing the vertex u and the edge record e that - points to v. Finally, if the stack empties without finding the desired vertex, - then the first loop ends, and the second main loop does not mark a path because - the stack is empty. - ****************************************************************************/ - -int _TestForZtoWPath(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; -int v, e, w; - - sp_ClearStack(theGraph->theStack); - sp_Push2(theGraph->theStack, IC->w, NIL); - - while (!sp_IsEmpty(theGraph->theStack)) - { - sp_Pop2(theGraph->theStack, v, e); - - if (gp_IsNotArc(e)) - { - // If the vertex is visited, then it is a member of the X-Y path - // Because it is being popped, its obstruction type is unknown because - // that is the only kind of vertex pushed. - // Hence, we break because we've found the desired path. - if (gp_GetVertexVisited(theGraph, v)) - break; - - // Mark this vertex as being visited by this method (i.e. ineligible - // to have processing started on it again) - gp_SetVertexVisitedInfo(theGraph, v, -1); - - e = gp_GetFirstArc(theGraph, v); - } - else - e = gp_GetNextArc(theGraph, e); - - // This while loop breaks on the first edge it finds that is eligible to be - // pushed. Once that happens, we break. The successive edges of a vertex are - // only pushed (see the else clause above) once all paths extending from v - // through e have been explored and found not to contain the desired path - while (gp_IsArc(e)) - { - w = gp_GetNeighbor(theGraph, e); - - // The test for w being a virtual vertex is just safeguarding the two subsequent calls, - // but it can never happen due to the obstructing X-Y path. - if (gp_IsNotVirtualVertex(theGraph, w) && - gp_GetVertexVisitedInfo(theGraph, w) != -1 && - gp_GetVertexObstructionType(theGraph, w) == VERTEX_OBSTRUCTIONTYPE_UNKNOWN) - { - sp_Push2(theGraph->theStack, v, e); - sp_Push2(theGraph->theStack, w, NIL); - - break; - } - - e = gp_GetNextArc(theGraph, e); - } - } - - while (!sp_IsEmpty(theGraph->theStack)) - { - sp_Pop2(theGraph->theStack, v, e); - gp_SetVertexVisited(theGraph, v); - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - return OK; -} - -/**************************************************************************** - _TestForStraddlingBridge() - We proceed on the path [V...u_{max}) from the current vertex V up to and - excluding u_{max}. For each vertex p, we test whether p has a least - ancestor less than u_{max} and whether p has a DFS child c that is not an - ancestor of X, Y and W and that has a connection to an ancestor of u_{max} - (in other words, whether the child C has a lowpoint less than u_{max}). - - The sortedDFSChildLIst of the vertex p is scanned for the separated DFS - child c of least lowpoint, excluding the ancestor of X, Y and W. - - If no bridge straddling u_{max} is found, the function returns NIL. - If a straddling bridge is found, the function returns a descendant d - of p in the subtree rooted by c such that d has a leastAncestor less - than u_{max}. Given the vertex d, the path through the straddling - bridge required in Minors E6 and E7 is easy to identify: Mark the - DFS tree path from d to p, and add and mark the edge from d to its - least ancestor. - - OPTIMIZATION: If a straddling bridge is not found, then in each tree edge of - the path [V...u_{max}) we set the member noStraddle equal to u_{max}. - Then, we modify the above stated routine so that if it is testing - for a straddling bridge of u_{max} along this path, it will stop - if it encounters an edge with noStraddle equal to u_{max}. - Also, the optimization will only set noStraddle equal to - u_{max} on the portion of the path that is traversed. Finally, if - noStraddle is set to a value other than NIL, the setting will be - ignored and it will not be changed. - - Due to this optimization, we do not traverse a path more than once - to find out whether a vertex on the path has a bridge that straddles - u_{max}. This leaves two questions: - 1) What if a future step must determine whether there is a - straddling bridge of an ancestor of u_{max}? - 2) What if a future step must determine whether there is a - straddling bridge of a descendant of u_{max}? - - The condition described in the first question cannot occur because it - would imply the ability to detect a straddling bridge now. - The condition described by the second question may occur, but in the - future step, the bicomp now being tested for a K_{3,3} will be part of - a straddling bridge in that future step. Thus, the straddling - bridge query is asked at most twice along any DFS tree path. - ****************************************************************************/ - -int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max) -{ -isolatorContextP IC = &theGraph->IC; -int p, c, d, excludedChild, e; - - p = IC->v; - excludedChild = gp_GetDFSChildFromRoot(theGraph, IC->r); - d = NIL; - - // Starting at V, traverse the ancestor path to u_max looking for a straddling bridge - while (p > u_max) - { - // If we find a direct edge from p to an ancestor of u_max, the break. - if (gp_GetVertexLeastAncestor(theGraph, p) < u_max) - { - d = p; - break; - } - - // Check for a path from p to an ancestor of u_max using the child of p - // with the least Lowpoint, except the child that is an ancestor of X, Y and W. - // It is possible to do this just using the sortedDFSChildList, but no point - // in not using the separatedDFSChildList - /* - { - int c = gp_GetVertexSortedDFSChildList(theGraph, p); - while (gp_IsVertex(c)) - { - if (c != excludedChild && gp_IsSeparatedDFSChild(theGraph, c)) - { - if (gp_GetVertexLowpoint(theGraph, c) < u_max) - break; - } - - c = gp_GetVertexNextDFSChild(theGraph, p, c); - } - } - */ - c = context->VI[p].separatedDFSChildList; - if (c == excludedChild) - c = LCGetNext(context->separatedDFSChildLists, c, c); - - if (gp_IsVertex(c) && gp_GetVertexLowpoint(theGraph, c) < u_max) - { - _FindUnembeddedEdgeToSubtree(theGraph, gp_GetVertexLowpoint(theGraph, c), c, &d); - break; - } - - // Check for noStraddle of u_max, break if found - e = gp_GetFirstArc(theGraph, p); - if (context->E[e].noStraddle == u_max) - break; - - // Go to the next ancestor - excludedChild = p; - p = gp_GetVertexParent(theGraph, p); - } - - // If d is NIL, then no straddling bridge was found, so we do the noStraddle optimization. - if (gp_IsNotVertex(d)) - { - c = IC->v; - while (c != p) - { - e = gp_GetFirstArc(theGraph, c); - if (gp_IsVertex(context->E[e].noStraddle)) - break; - - context->E[e].noStraddle = u_max; - - c = gp_GetVertexParent(theGraph, c); - } - } - - // Return either NIL indicating no bridge straddling u_max or the descendant d - // used to help mark a straddling bridge that was found by this test. - return d; -} - -/**************************************************************************** - _ReduceBicomp() - - We want to reduce the given biconnected component to a 4-cycle plus an - internal edge connecting X and Y. Each edge is to be associated with a - path from the original graph, preserving the depth first search tree - paths that help connect the vertices R, X, Y, and W. If a K_{3,3} is later found, - the paths are restored, but it is necessary to preserve the DFS tree so that - functions like MarkDFSPath() will be able to pass through the restored bicomp. - Also, if a K_{3,3} is later found due to the merge blocker optimization, then the - internal X-Y path may be needed and, once the bicomp reduction is reversed, - a full DFS subtree connecting all vertices in the bicomp will need to be - restored or else functions that traverse the bicomp will not work. - - For example, _FindK33WithMergeBlocker() invokes ChooseTypeOfNonplanarityMinor() - to help reconstruct the context under which the mergeBlocker was set. - ChooseTypeOfNonplanarityMinor() calls _ClearVisitedFlagsInBicomp(), which - depends on the DFS tree. - - NOTE: The following are some general steps taken in this method: - 1) All edges in the bicomp are marked unvisited - 2) selected paths are marked visited - 3) unvisited edges are deleted - 4) the edges of the bicomp are marked unvisited again - 5) the remaining paths of the bicomp are reduced - Some of the edges that get deleted in step 3 above may represent - paths that were reduced in prior embedder iterations. We delete - the reduction edge but not the path it represents. - If a K_{3,3} is ever found, then the edges of these reduced paths - are still in the graph, though not connected to anything important. - The desired K_{3,3} is marked visited, but step 4 above ensures that - these reduction paths are not marked visited. Hence, they will be - deleted when the K_{3,3} is isolated, and this routine does not - need to restore any reduced paths on the edges it deletes. - We also don't (and don't have the time to) restore any reduction - edges along the paths we intend to keep. - ****************************************************************************/ - -int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R) -{ -isolatorContextP IC = &theGraph->IC; -int min, mid, max, A, A_edge, B, B_edge; -int rxType, xwType, wyType, yrType, xyType; - -/* The vertices in the bicomp need to be oriented so that functions - like MarkPathAlongBicompExtFace() will work. */ - - if (_OrientVerticesInBicomp(theGraph, R, 0) != OK) - return NOTOK; - -/* The reduced edges start with a default type of 'tree' edge. The - tests below, which identify the additional non-tree paths - needed to complete the reduced bicomp, also identify which - reduced edges need to be cycle edges.*/ - - rxType = xwType = wyType = yrType = xyType = EDGE_TYPE_PARENT; - -/* Now we calculate some values that help figure out the shape of the - DFS subtree whose structure will be retained in the bicomp. */ - - min = MIN3(IC->x, IC->y, IC->w); - max = MAX3(IC->x, IC->y, IC->w); - mid = MAX3(MIN(IC->x, IC->y), MIN(IC->x, IC->w), MIN(IC->y, IC->w)); - -/* If the order of descendendancy from V goes first to X, then it can - proceed either to W then Y or to Y then W */ - - if (min == IC->x) - { - /* A is a descendant adjacent to the current vertex by a cycle edge - whose DFS tree path to either mid or max is combined with the - cycle edge to form the path that will be reduced to the - external face cycle edge (V, max). */ - - A_edge = gp_GetLastArc(theGraph, IC->r); - A = gp_GetNeighbor(theGraph, A_edge); - yrType = EDGE_TYPE_BACK; - - /* If Y is max, then a path parallel to the X-Y path will be a - second path reduced to a cycle edge. We find the neighbor B - of min=X on the X-Y path. The edge (B, min) is a cycle edge - that, along with the DFS tree path (B, ..., max), will be - retained and reduced to a cycle edge. */ - - if (max == IC->y) - { - B_edge = gp_GetLastArc(theGraph, IC->x); - while (B_edge != gp_GetFirstArc(theGraph, IC->x)) - { - if (gp_GetEdgeVisited(theGraph, B_edge)) break; - B_edge = gp_GetPrevArc(theGraph, B_edge); - } - - if (!gp_GetEdgeVisited(theGraph, B_edge)) - return NOTOK; - - B = gp_GetNeighbor(theGraph, B_edge); - xyType = EDGE_TYPE_BACK; - } - - /* Otherwise, W is max so we find the neighbor B of min=X on the - lower external face path (X, ..., W), which excludes V. The - cycle edge (B, min) and the DFS tree path (B, max) will be - retained and reduced to a cycle edge.*/ - - else if (max == IC->w) - { - B_edge = gp_GetFirstArc(theGraph, IC->x); - B = gp_GetNeighbor(theGraph, B_edge); - xwType = EDGE_TYPE_BACK; - } - - else return NOTOK; - } - -/* Otherwise, the order of descendancy from V goes first to Y, then it - proceeds to either W then X or to X then W. The */ - - else - { - A_edge = gp_GetFirstArc(theGraph, IC->r); - A = gp_GetNeighbor(theGraph, A_edge); - rxType = EDGE_TYPE_BACK; - - if (max == IC->x) - { - B_edge = gp_GetFirstArc(theGraph, IC->y); - while (B_edge != gp_GetLastArc(theGraph, IC->y)) - { - if (gp_GetEdgeVisited(theGraph, B_edge)) break; - B_edge = gp_GetNextArc(theGraph, B_edge); - } - - if (!gp_GetEdgeVisited(theGraph, B_edge)) - return NOTOK; - - B = gp_GetNeighbor(theGraph, B_edge); - xyType = EDGE_TYPE_BACK; - } - - else if (max == IC->w) - { - B_edge = gp_GetLastArc(theGraph, IC->y); - B = gp_GetNeighbor(theGraph, B_edge); - wyType = EDGE_TYPE_BACK; - } - - else return NOTOK; - } - -/* Now that we have collected the information on which cycle edge and - which tree paths will actually be retained, we clear the visited - flags so the current X-Y path will not be retained (an X-Y path - formed mostly or entirely from DFS tree edges is retained). */ - - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - -/* Now we mark the tree path from the maximum numbered vertex up - to the bicomp root. This marks one of the following four paths: - Case 1. (V, ..., X=min, ..., W=mid, ..., Y=max) - Case 2. (V, ..., X=min, ..., Y=mid, ..., W=max) - Case 3. (V, ..., Y=min, ..., W=mid, ..., X=max) - Case 4. (V, ..., Y=min, ..., X=mid, ..., W=max) */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, R, max) != OK) - return NOTOK; - -/* Now we use A to mark a path on the external face corresponding to: - Case 1. (V, ..., Y=max) - Case 2. (V, ..., Y=mid) - Case 3. (V, ..., X=max) - Case 4. (V, ..., X=mid) */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, min==IC->x ? IC->y : IC->x, A) != OK) - return NOTOK; - - gp_SetEdgeVisited(theGraph, A_edge); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, A_edge)); - -/* Now we use B to mark either an X-Y path or a path of the external face - corresponding to: - Case 1. (X=min, ..., B, ..., Y=max) - Case 2. (X=min, ..., B, ..., W=max) - Case 3. (Y=min, ..., B, ..., X=max) - Case 4. (Y=min, ..., B, ..., W=max) */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, max, B) != OK) - return NOTOK; - - gp_SetEdgeVisited(theGraph, B_edge); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, B_edge)); - -/* Delete the unmarked edges in the bicomp. Note that if an unmarked edge - * represents a reduced path, then only the reduction edge is deleted here. - * The path it represents is only deleted later (see NOTE above) */ - - if (_K33Search_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK) - return NOTOK; - -/* Clear all visited flags in the bicomp. - This is the important "step 4" mentioned in the NOTE above */ - - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - -/* Clear all orientation signs in the bicomp. - Note that the whole bicomp may not be properly oriented at this point - because we may have exchanged external face paths for internal - DFS tree paths. However, the reduced bicomp will be properly - oriented, and the paths of degree 2 vertices will have their - orientations fixed if/when reduction edges are restored. */ - - if (_ClearInvertedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - -/* Reduce the paths to single edges. - Note that although the whole bicomp may not be properly oriented at this - point (as noted above), the four principal vertices R, X, W and Y still - are consistently oriented with one another, e.g. R's link[0] indicates - the external face path toward X that excludes W and Y, and X's link[1] - indicates that same path. */ - - if (_ReduceExternalFacePathToEdge(theGraph, context, R, IC->x, rxType) != OK || - _ReduceExternalFacePathToEdge(theGraph, context, IC->x, IC->w, xwType) != OK || - _ReduceExternalFacePathToEdge(theGraph, context, IC->w, IC->y, wyType) != OK || - _ReduceExternalFacePathToEdge(theGraph, context, IC->y, R, yrType) != OK) - return NOTOK; - - if (_ReduceXYPathToEdge(theGraph, context, IC->x, IC->y, xyType) != OK) - return NOTOK; - - return OK; -} - -/******************************************************************** - Edge deletion that occurs during a reduction or restoration of a - reduction is augmented by clearing the K_{3,3} search-specific - data members. This is augmentation is not needed in the delete edge - operations that happen once a K_{3,3} homeomorph has been found and - marked for isolation. - ********************************************************************/ - -int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink) -{ - _K33Search_InitEdgeRec(context, e); - _K33Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e)); - - return gp_DeleteEdge(theGraph, e, nextLink); -} - -/******************************************************************** - _K33Search_DeleteUnmarkedEdgesInBicomp() - - This function deletes from a given biconnected component all edges - whose visited member is zero. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls - the overloaded _K33_DeleteEdge() rather than gp_DeleteEdge() - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetEdgeVisited(theGraph, e) - ? gp_GetNextArc(theGraph, e) - : _K33Search_DeleteEdge(theGraph, context, e, 0); - } - } - return OK; -} - -/**************************************************************************** - _ReduceExternalFacePathToEdge() - ****************************************************************************/ - -int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType) -{ -int prevLink, v, w, e; - - /* If the path is a single edge, then no need for a reduction */ - - prevLink = 1; - v = _GetNeighborOnExtFace(theGraph, u, &prevLink); - if (v == x) - { - gp_SetExtFaceVertex(theGraph, u, 0, x); - gp_SetExtFaceVertex(theGraph, x, 1, u); - return OK; - } - - /* We have the endpoints u and x of the path, and we just computed the - first vertex internal to the path and a neighbor of u. Now we - compute the vertex internal to the path and a neighbor of x. */ - - prevLink = 0; - w = _GetNeighborOnExtFace(theGraph, x, &prevLink); - - /* Delete the two edges that connect the path to the bicomp. - If either edge is a reduction edge, then we have to restore - the path it represents. We can only afford to visit the - endpoints of the path. - Note that in the restored path, the edge incident to each - endpoint of the original path is a newly added edge, - not a reduction edge. */ - - e = gp_GetFirstArc(theGraph, u); - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetFirstArc(theGraph, u); - v = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - e = gp_GetLastArc(theGraph, x); - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetLastArc(theGraph, x); - w = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Add the reduction edge, then set its path connectors so the original - path can be recovered and set the edge type so the essential structure - of the DFS tree can be maintained (The 'Do X to Bicomp' functions - and functions like MarkDFSPath(0 depend on this). */ - - gp_AddEdge(theGraph, u, 0, x, 1); - - e = gp_GetFirstArc(theGraph, u); - context->E[e].pathConnector = v; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType)); - - e = gp_GetLastArc(theGraph, x); - context->E[e].pathConnector = w; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType)); - - /* Set the external face info */ - - gp_SetExtFaceVertex(theGraph, u, 0, x); - gp_SetExtFaceVertex(theGraph, x, 1, u); - - return OK; -} - -/**************************************************************************** - _ReduceXYPathToEdge() - ****************************************************************************/ - -int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType) -{ -int e, v, w; - - e = gp_GetFirstArc(theGraph, u); - e = gp_GetNextArc(theGraph, e); - v = gp_GetNeighbor(theGraph, e); - - /* If the XY-path is a single edge, then no reduction is needed */ - - if (v == x) - return OK; - - /* Otherwise, remove the two edges that join the XY-path to the bicomp */ - - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetFirstArc(theGraph, u); - e = gp_GetNextArc(theGraph, e); - v = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - e = gp_GetFirstArc(theGraph, x); - e = gp_GetNextArc(theGraph, e); - w = gp_GetNeighbor(theGraph, e); - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetFirstArc(theGraph, x); - e = gp_GetNextArc(theGraph, e); - w = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Now add a single edge to represent the XY-path */ - gp_InsertEdge(theGraph, u, gp_GetFirstArc(theGraph, u), 0, - x, gp_GetFirstArc(theGraph, x), 0); - - /* Now set up the path connectors so the original XY-path can be recovered if needed. - Also, set the reduction edge's type to preserve the DFS tree structure */ - - e = gp_GetFirstArc(theGraph, u); - e = gp_GetNextArc(theGraph, e); - context->E[e].pathConnector = v; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType)); - - e = gp_GetFirstArc(theGraph, x); - e = gp_GetNextArc(theGraph, e); - context->E[e].pathConnector = w; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType)); - - return OK; -} - -/**************************************************************************** - _RestoreReducedPath() - Given an edge record of an edge used to reduce a path, we want to restore - the path in constant time. - The path may contain more reduction edges internally, but we do not - search for and process those since it would violate the constant time - bound required of this function. - return OK on success, NOTOK on failure - ****************************************************************************/ - -int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e) -{ -int eTwin, u, v, w, x; -int e0, e1, eTwin0, eTwin1; - - if (gp_IsNotVertex(context->E[e].pathConnector)) - return OK; - - eTwin = gp_GetTwinArc(theGraph, e); - - u = gp_GetNeighbor(theGraph, eTwin); - v = context->E[e].pathConnector; - w = context->E[eTwin].pathConnector; - x = gp_GetNeighbor(theGraph, e); - - /* Get the locations of the edge records between which the new - edge records must be added in order to reconnect the path - parallel to the edge. */ - - e0 = gp_GetNextArc(theGraph, e); - e1 = gp_GetPrevArc(theGraph, e); - eTwin0 = gp_GetNextArc(theGraph, eTwin); - eTwin1 = gp_GetPrevArc(theGraph, eTwin); - - /* We first delete the edge represented by e and eTwin. We do so before - restoring the path to ensure we do not exceed the maximum arc capacity. */ - - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Now we add the two edges to reconnect the reduced path represented - by the edge [e, eTwin]. The edge record in u is added between e0 and e1. - Likewise, the new edge record in x is added between eTwin0 and eTwin1. */ - - if (gp_IsArc(e0)) - { - if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) - return NOTOK; - } - - if (gp_IsArc(eTwin0)) - { - if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) - return NOTOK; - } - - // Set the types of the newly added edges. In both cases, the first of the two - // vertex parameters is known to be degree 2 because they are internal to the - // path being restored, so this operation is constant time. - if (_SetEdgeType(theGraph, v, u) != OK || - _SetEdgeType(theGraph, w, x) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _RestoreAndOrientReducedPaths() - This function searches the embedding for any edges that are specially marked - as being representative of a path that was previously reduced to a - single edge by _ReduceBicomp(). The edge is replaced by the path. - Note that the new path may contain more reduction edges, and these will be - iteratively expanded by the outer for loop. - - If the edge records of an edge being expanded are the first or last arcs - of the edge's vertex endpoints, then the edge may be along the external face. - If so, then the vertices along the path being restored must be given a - consistent orientation with the endpoints. It is expected that the embedding - will have been oriented prior to this operation. - ****************************************************************************/ - -int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context) -{ - int EsizeOccupied, e, eTwin, u, v, w, x, visited; - int e0, eTwin0, e1, eTwin1; - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;) - { - if (gp_IsVertex(context->E[e].pathConnector)) - { - visited = gp_GetEdgeVisited(theGraph, e); - - eTwin = gp_GetTwinArc(theGraph, e); - u = gp_GetNeighbor(theGraph, eTwin); - v = context->E[e].pathConnector; - w = context->E[eTwin].pathConnector; - x = gp_GetNeighbor(theGraph, e); - - /* Now we need the predecessor and successor edge records - of e and eTwin. The edge (u, v) will be inserted so - that the record in u's adjacency list that indicates v - will be between e0 and e1. Likewise, the edge record - (x -> w) will be placed between eTwin0 and eTwin1. */ - - e0 = gp_GetNextArc(theGraph, e); - e1 = gp_GetPrevArc(theGraph, e); - eTwin0 = gp_GetNextArc(theGraph, eTwin); - eTwin1 = gp_GetPrevArc(theGraph, eTwin); - - /* We first delete the edge represented by e and eTwin. We do so before - restoring the path to ensure we do not exceed the maximum arc capacity. */ - - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Now we add the two edges to reconnect the reduced path represented - by the edge [e, eTwin]. The edge record in u is added between e0 and e1. - Likewise, the new edge record in x is added between eTwin0 and eTwin1. */ - - if (gp_IsArc(e0)) - { - if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) - return NOTOK; - } - - if (gp_IsArc(eTwin0)) - { - if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) - return NOTOK; - } - - /* Set the types of the newly added edges */ - - if (_SetEdgeType(theGraph, u, v) != OK || - _SetEdgeType(theGraph, w, x) != OK) - return NOTOK; - - /* We determine whether the reduction edge may be on the external face, - in which case we will need to ensure that the vertices on the path - being restored are consistently oriented. This will accommodate - future invocations of MarkPathAlongBicompExtFace(). - Note: If e0, e1, eTwin0 or eTwin1 is not an edge, then it is - because we've walked off the end of the edge record list, - which happens when e and eTwin are either the first or - last edge of the containing vertex. In turn, the first - and last edges of a vertex are the ones that hold it onto - the external face, if it is on the external face. */ - - if ((gp_IsNotArc(e0) && gp_IsNotArc(eTwin1)) || (gp_IsNotArc(e1) && gp_IsNotArc(eTwin0))) - { - if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - - /* The internal XY path was already marked as part of the decision logic - that made us decide we could find a K_{3,3} and hence that we should - reverse all of the reductions. Subsequent code counts on the fact - that the X-Y path is already marked, so if we replace a marked edge - with a path, then we need to mark the path. Similarly, for an unmarked - edge, the replacement path should be unmarked. */ - - if (visited) - { - if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - else - { - if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - } - else e+=2; - } - - return OK; -} - -/**************************************************************************** - _MarkStraddlingBridgePath() - ****************************************************************************/ - -int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d) -{ -isolatorContextP IC = &theGraph->IC; -int p, e; - -/* Find the point of intersection p between the path (v ... u_max) - and the path (d ... u_max). */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, u_max, IC->r) != OK) - return NOTOK; - - p = d; - while (!gp_GetVertexVisited(theGraph, p)) - { - gp_SetVertexVisited(theGraph, p); - - e = gp_GetFirstArc(theGraph, p); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - break; - - e = gp_GetNextArc(theGraph, e); - } - - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - - p = gp_GetNeighbor(theGraph, e); - - /* If p is a root copy, mark it visited and skip to the parent copy */ - if (gp_IsVirtualVertex(theGraph, p)) - { - gp_SetVertexVisited(theGraph, p); - p = gp_GetPrimaryVertexFromRoot(theGraph, p); - } - } - -/* Unmark the path (p ... u_max), which was marked to help find p. - The path from v to u_{max} is not needed to form a K_{3,3} except - for the portion of the path up to p that, with the straddling - bridge path, comprises part of the connection to u_d. In the - minor, the path between v and p is edge contracted. */ - - while (p != u_max) - { - e = gp_GetFirstArc(theGraph, p); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - break; - - e = gp_GetNextArc(theGraph, e); - } - - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - - p = gp_GetNeighbor(theGraph, e); - gp_ClearVertexVisited(theGraph, p); - - /* If p is a root copy, clear its visited flag and skip to the - parent copy */ - - if (gp_IsVirtualVertex(theGraph, p)) - { - p = gp_GetPrimaryVertexFromRoot(theGraph, p); - gp_ClearVertexVisited(theGraph, p); - } - } - -/* The straddling bridge must join the path (u_max ... u_min). If u_d is an - ancestor of u_min, then mark the path that joins u_d to u_min. */ - - if (u_d < u_min) - if (theGraph->functions.fpMarkDFSPath(theGraph, u_d, u_min) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE5() - The paths (x, w), (y, w) and (v, u_{max}) are not needed. - The x-y path and the internal w-z path are already marked. - ****************************************************************************/ - -int _IsolateMinorE5(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux,IC->uy,IC->uz), - MAX3(IC->ux,IC->uy,IC->uz)) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE6() - The paths (x, y), (v, w) and (v, u_{max}) are not needed. - The path through the straddling bridge that connects from an ancestor of - u_{max} to v is required, but it may connnect to an ancestor p of v. - In such a case, the path (v, p) is required, while (p, u_{max}) is not. - ****************************************************************************/ - -int _IsolateMinorE6(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; -int u_min, u_max, d, u_d; - -/* Clear the previously marked x-y path */ - - if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) - return NOTOK; - -/* Clear dw to stop the marking of path (v, w) */ - - IC->dw = NIL; - -/* Mark (v, ..., x, ..., w, ..., y, ... v) */ - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK) - return NOTOK; - -/* Mark the path through the straddling bridge (except for the final - edge (u_d, d) which is added last by convention). */ - - u_min = MIN3(IC->ux,IC->uy,IC->uz); - u_max = MAX3(IC->ux,IC->uy,IC->uz); - d = _TestForStraddlingBridge(theGraph, context, u_max); - u_d = gp_GetVertexLeastAncestor(theGraph, d); - - if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK) - return NOTOK; - -/* Make the final markings and edge additions */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK || - _AddAndMarkEdge(theGraph, u_d, d) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE7() - ****************************************************************************/ - -int _IsolateMinorE7(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; -int u_min, u_max, d, u_d; - -/* Mark the appropriate two portions of the external face depending on - symmetry condition */ - - if (IC->uy < IC->ux) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) - return NOTOK; - } - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK) - return NOTOK; - } - -/* Mark the path through the straddling bridge (except for the final - edge (u_d, d) which is added last by convention). */ - - u_min = MIN3(IC->ux,IC->uy,IC->uz); - u_max = MAX3(IC->ux,IC->uy,IC->uz); - d = _TestForStraddlingBridge(theGraph, context, u_max); - u_d = gp_GetVertexLeastAncestor(theGraph, d); - - if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK) - return NOTOK; - -/* Make the final markings and edge additions */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK || - _AddAndMarkEdge(theGraph, u_d, d) != OK) - return NOTOK; - - return OK; -} diff --git a/planarity_src/c/graphK33Search.h b/planarity_src/c/graphK33Search.h deleted file mode 100644 index e273d90..0000000 --- a/planarity_src/c/graphK33Search.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef GRAPH_K33SEARCH_H -#define GRAPH_K33SEARCH_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 - diff --git a/planarity_src/c/graphK33Search.private.h b/planarity_src/c/graphK33Search.private.h deleted file mode 100644 index 6a5c50f..0000000 --- a/planarity_src/c/graphK33Search.private.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef GRAPH_K33SEARCH_PRIVATE_H -#define GRAPH_K33SEARCH_PRIVATE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 - diff --git a/planarity_src/c/graphK33Search_Extensions.c b/planarity_src/c/graphK33Search_Extensions.c deleted file mode 100644 index 46f61e9..0000000 --- a/planarity_src/c/graphK33Search_Extensions.c +++ /dev/null @@ -1,794 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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; -} - diff --git a/planarity_src/c/graphK4Search.c b/planarity_src/c/graphK4Search.c deleted file mode 100644 index 1ae1c15..0000000 --- a/planarity_src/c/graphK4Search.c +++ /dev/null @@ -1,1512 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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; -} diff --git a/planarity_src/c/graphK4Search.h b/planarity_src/c/graphK4Search.h deleted file mode 100644 index 3d3621d..0000000 --- a/planarity_src/c/graphK4Search.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef GRAPH_K4SEARCH_H -#define GRAPH_K4SEARCH_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 - diff --git a/planarity_src/c/graphK4Search.private.h b/planarity_src/c/graphK4Search.private.h deleted file mode 100644 index d2e3377..0000000 --- a/planarity_src/c/graphK4Search.private.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef GRAPH_K4SEARCH_PRIVATE_H -#define GRAPH_K4SEARCH_PRIVATE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 diff --git a/planarity_src/c/graphK4Search_Extensions.c b/planarity_src/c/graphK4Search_Extensions.c deleted file mode 100644 index 6210c3f..0000000 --- a/planarity_src/c/graphK4Search_Extensions.c +++ /dev/null @@ -1,537 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include - -#include "graphK4Search.private.h" -#include "graphK4Search.h" - -extern int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R); - -extern int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, - int *degrees, int *imageVerts); - -extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, - int *imageVerts, int maxNumImageVerts); - -extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); - -/* Forward declarations of local functions */ - -void _K4Search_ClearStructures(K4SearchContext *context); -int _K4Search_CreateStructures(K4SearchContext *context); -int _K4Search_InitStructures(K4SearchContext *context); - -void _K4Search_InitEdgeRec(K4SearchContext *context, int e); - -/* Forward declarations of overloading functions */ -int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); - -int _K4Search_InitGraph(graphP theGraph, int N); -void _K4Search_ReinitializeGraph(graphP theGraph); -int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -/* Forward declarations of functions used by the extension system */ - -void *_K4Search_DupContext(void *pContext, void *theGraph); -void _K4Search_FreeContext(void *); - -/**************************************************************************** - * K4SEARCH_ID - the variable used to hold the integer identifier for this - * extension, enabling this feature's extension context to be distinguished - * from other features' extension contexts that may be attached to a graph. - ****************************************************************************/ - -int K4SEARCH_ID = 0; - -/**************************************************************************** - gp_AttachK4Search() - - This function adjusts the graph data structure to attach the K4 search - feature. - ****************************************************************************/ - -int gp_AttachK4Search(graphP theGraph) -{ - K4SearchContext *context = NULL; - - // If the K4 search feature has already been attached to the graph, - // then there is no need to attach it again - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - if (context != NULL) - { - return OK; - } - - // Allocate a new extension context - context = (K4SearchContext *) malloc(sizeof(K4SearchContext)); - if (context == NULL) - { - return NOTOK; - } - - // First, tell the context that it is not initialized - context->initialized = 0; - - // Save a pointer to theGraph in the context - context->theGraph = theGraph; - - // Put the overload functions into the context function table. - // gp_AddExtension will overload the graph's functions with these, and - // return the base function pointers in the context function table - memset(&context->functions, 0, sizeof(graphFunctionTable)); - context->functions.fpHandleBlockedBicomp = _K4Search_HandleBlockedBicomp; - context->functions.fpEmbedPostprocess = _K4Search_EmbedPostprocess; - context->functions.fpCheckEmbeddingIntegrity = _K4Search_CheckEmbeddingIntegrity; - context->functions.fpCheckObstructionIntegrity = _K4Search_CheckObstructionIntegrity; - - context->functions.fpInitGraph = _K4Search_InitGraph; - context->functions.fpReinitializeGraph = _K4Search_ReinitializeGraph; - context->functions.fpEnsureArcCapacity = _K4Search_EnsureArcCapacity; - - _K4Search_ClearStructures(context); - - // Store the K4 search context, including the data structure and the - // function pointers, as an extension of the graph - if (gp_AddExtension(theGraph, &K4SEARCH_ID, (void *) context, - _K4Search_DupContext, _K4Search_FreeContext, - &context->functions) != OK) - { - _K4Search_FreeContext(context); - return NOTOK; - } - - // Create the K4-specific structures if the size of the graph is known - // Attach functions are always invoked after gp_New(), but if a graph - // extension must be attached before gp_Read(), then the attachment - // also happens before gp_InitGraph(), which means N==0. - // However, sometimes a feature is attached after gp_InitGraph(), in - // which case N > 0 - if (theGraph->N > 0) - { - if (_K4Search_CreateStructures(context) != OK || - _K4Search_InitStructures(context) != OK) - { - _K4Search_FreeContext(context); - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - gp_DetachK4Search() - ********************************************************************/ - -int gp_DetachK4Search(graphP theGraph) -{ - return gp_RemoveExtension(theGraph, K4SEARCH_ID); -} - -/******************************************************************** - _K4Search_ClearStructures() - ********************************************************************/ - -void _K4Search_ClearStructures(K4SearchContext *context) -{ - if (!context->initialized) - { - // Before initialization, the pointers are stray, not NULL - // Once NULL or allocated, free() or LCFree() can do the job - context->E = NULL; - - context->handlingBlockedBicomp = FALSE; - - context->initialized = 1; - } - else - { - if (context->E != NULL) - { - free(context->E); - context->E = NULL; - } - context->handlingBlockedBicomp = FALSE; - } -} - -/******************************************************************** - _K4Search_CreateStructures() - Create uninitialized structures for the vertex and edge levels, and - initialized structures for the graph level - ********************************************************************/ -int _K4Search_CreateStructures(K4SearchContext *context) -{ - int Esize = gp_EdgeIndexBound(context->theGraph); - - if (context->theGraph->N <= 0) - return NOTOK; - - if ((context->E = (K4Search_EdgeRecP) malloc(Esize*sizeof(K4Search_EdgeRec))) == NULL || - 0) - { - return NOTOK; - } - - return OK; -} - -/******************************************************************** - _K4Search_InitStructures() - ********************************************************************/ -int _K4Search_InitStructures(K4SearchContext *context) -{ -#if NIL == 0 || NIL == -1 - memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K4Search_EdgeRec)); -#else - int e, Esize; - - Esize = gp_EdgeIndexBound(context->theGraph); - for (e = gp_GetFirstEdge(context->theGraph); e < Esize; e++) - _K4Search_InitEdgeRec(context, e); -#endif - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_InitGraph(graphP theGraph, int N) -{ - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context == NULL) - return NOTOK; - - theGraph->N = N; - theGraph->NV = N; - if (theGraph->arcCapacity == 0) - theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N; - - if (_K4Search_CreateStructures(context) != OK || - _K4Search_InitStructures(context) != OK) - return NOTOK; - - context->functions.fpInitGraph(theGraph, N); - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -void _K4Search_ReinitializeGraph(graphP theGraph) -{ - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - // Reinitialize the graph - context->functions.fpReinitializeGraph(theGraph); - - // Do the reinitialization that is specific to this module - _K4Search_InitStructures(context); - } -} - -/******************************************************************** - The current implementation does not support an increase of arc - (edge record) capacity once the extension is attached to the graph - data structure. This is only due to not being necessary to support. - For now, it is easy to ensure the correct capacity before attaching - the extension, but support could be added later if there is some - reason to do so. - ********************************************************************/ - -int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ - return NOTOK; -} - -/******************************************************************** - _K4Search_DupContext() - ********************************************************************/ - -void *_K4Search_DupContext(void *pContext, void *theGraph) -{ - K4SearchContext *context = (K4SearchContext *) pContext; - K4SearchContext *newContext = (K4SearchContext *) malloc(sizeof(K4SearchContext)); - - if (newContext != NULL) - { - int Esize = gp_EdgeIndexBound((graphP) theGraph); - - *newContext = *context; - - newContext->theGraph = (graphP) theGraph; - - newContext->initialized = 0; - _K4Search_ClearStructures(newContext); - if (((graphP) theGraph)->N > 0) - { - if (_K4Search_CreateStructures(newContext) != OK) - { - _K4Search_FreeContext(newContext); - return NULL; - } - - memcpy(newContext->E, context->E, Esize*sizeof(K4Search_EdgeRec)); - } - } - - return newContext; -} - -/******************************************************************** - _K4Search_FreeContext() - ********************************************************************/ - -void _K4Search_FreeContext(void *pContext) -{ - K4SearchContext *context = (K4SearchContext *) pContext; - - _K4Search_ClearStructures(context); - free(pContext); -} - -/******************************************************************** - ********************************************************************/ - -void _K4Search_InitEdgeRec(K4SearchContext *context, int e) -{ - context->E[e].pathConnector = NIL; -} - -/******************************************************************** - _K4Search_HandleBlockedBicomp() - Returns OK if no K4 homeomorph found and blockage cleared (OK to - proceed with Walkdown embedding) - NONEMBEDDABLE if K4 homeomorph found, and Walkdown embedding - should be terminated. - NOTOK on internal error - ********************************************************************/ - -int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) -{ - K4SearchContext *context = NULL; - - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - if (context == NULL) - return NOTOK; - - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - int RetVal = OK; - - // If invoked on a descendant bicomp, then we push its root then search once - // since not finding a K4 homeomorph will also clear the blockage and allow - // the Walkdown to continue walking down - if (R != RootVertex) - { - sp_Push2(theGraph->theStack, R, 0); - if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, R)) == OK) - { - // If the Walkdown will be told it is OK to continue, then we have to take the descendant - // bicomp root back off the stack so the Walkdown can try to descend to it again. - int dummy; - sp_Pop2(theGraph->theStack, R, dummy); - - // And we have to clear the indicator of the minor A that was reduced, since it was eliminated. - theGraph->IC.minorType = 0; - } - } - - // Otherwise, if invoked on a child bicomp rooted by a virtual copy of v, - // then we search for a K4 homeomorph, and if OK is returned, then that indicates - // the blockage has been cleared and it is OK to Walkdown the bicomp. - // But the Walkdown finished, already, so we launch it again. - // If the Walkdown returns OK then all forward arcs were embedded. If NONEMBEDDABLE - // is returned, then the bicomp got blocked again, so we have to reiterate the K4 search - else - { - // If Walkdown has recursively called this handler on the bicomp rooted by RootVertex, - // then it is still blocked, so we just return NONEMBEDDABLE, which causes Walkdown to - // return to the loop below and signal that the loop should invoke the Walkdown again. - if (context->handlingBlockedBicomp) - return NONEMBEDDABLE; - - context->handlingBlockedBicomp = TRUE; - do { - // Detect whether bicomp can be used to find a K4 homeomorph. It it does, then - // it returns NONEMBEDDABLE so we break the search because we found the desired K4 - // If OK is returned, then the blockage was cleared and it is OK to Walkdown again. - if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, RootVertex)) != OK) - break; - - // Walkdown again to embed more edges. If Walkdown returns OK, then all remaining - // edges to its descendants are embedded, so we'll get out of this loop. If Walkdown - // detects that it still has not embedded all the edges to descendants of the bicomp's - // root edge child, then Walkdown calls this routine again, and the above non-reentrancy - // code returns NONEMBEDDABLE, causing this loop to search again for a K4. - theGraph->IC.minorType = 0; - RetVal = theGraph->functions.fpWalkDown(theGraph, v, RootVertex); - - // Except if the Walkdown returns NONEMBEDDABLE due to finding a K4 homeomorph entangled - // with a descendant bicomp (the R != RootVertex case above), then it was found - // entangled with Minor A, so we can stop the search if minor A is detected - if (theGraph->IC.minorType & MINORTYPE_A) - break; - - } while (RetVal == NONEMBEDDABLE); - context->handlingBlockedBicomp = FALSE; - } - - return RetVal; - } - else - { - return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) -{ - // For K4 search, we just return the edge embedding result because the - // search result has been obtained already. - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - return edgeEmbeddingResult; - } - - // When not searching for K4, we let the superclass do the work - else - { - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) -{ - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - return OK; - } - - // When not searching for K4, we let the superclass do the work - else - { - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) -{ - // When searching for K4, we ensure that theGraph is a subgraph of - // the original graph and that it contains a K4 homeomorph - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - int degrees[4], imageVerts[4]; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - return NOTOK; - - if (_getImageVertices(theGraph, degrees, 3, imageVerts, 4) != OK) - return NOTOK; - - if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE) - { - return OK; - } - - return NOTOK; - } - - // When not searching for K4, we let the superclass do the work - else - { - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} diff --git a/planarity_src/c/graphLib.h b/planarity_src/c/graphLib.h new file mode 100644 index 0000000..ccd6f45 --- /dev/null +++ b/planarity_src/c/graphLib.h @@ -0,0 +1,25 @@ +#ifndef HELPERSTUB_GRAPHLIB_H +#define HELPERSTUB_GRAPHLIB_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +// NOTE: This helper stub has been added to make it easier for downstream +// consumers to obtain access to all features in the graph library of +// the planarity project. + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "c/graphLib/graphLib.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/planarity_src/c/graphExtensions.c b/planarity_src/c/graphLib/extensionSystem/graphExtensions.c similarity index 79% rename from planarity_src/c/graphExtensions.c rename to planarity_src/c/graphLib/extensionSystem/graphExtensions.c index cfee91c..97cfa1e 100644 --- a/planarity_src/c/graphExtensions.c +++ b/planarity_src/c/graphLib/extensionSystem/graphExtensions.c @@ -1,589 +1,544 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include + +#include "../lowLevelUtils/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); -} + ********************************************************************/ + +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 **currFunctionTable = (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 = currFunctionTable[K]; + currFunctionTable[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 = NULL, curr = NULL, next = NULL; + + if (theGraph == NULL || moduleID == 0) + return NOTOK; + + 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); +} diff --git a/planarity_src/c/graphLib/extensionSystem/graphExtensions.h b/planarity_src/c/graphLib/extensionSystem/graphExtensions.h new file mode 100644 index 0000000..21dc720 --- /dev/null +++ b/planarity_src/c/graphLib/extensionSystem/graphExtensions.h @@ -0,0 +1,37 @@ +#ifndef GRAPH_EXTENSIONS_H +#define GRAPH_EXTENSIONS_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/extensionSystem/graphExtensions.private.h b/planarity_src/c/graphLib/extensionSystem/graphExtensions.private.h new file mode 100644 index 0000000..dc1e89d --- /dev/null +++ b/planarity_src/c/graphLib/extensionSystem/graphExtensions.private.h @@ -0,0 +1,35 @@ +#ifndef GRAPH_EXTENSIONS_PRIVATE_H +#define GRAPH_EXTENSIONS_PRIVATE_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/extensionSystem/graphFunctionTable.h b/planarity_src/c/graphLib/extensionSystem/graphFunctionTable.h new file mode 100644 index 0000000..bbc37df --- /dev/null +++ b/planarity_src/c/graphLib/extensionSystem/graphFunctionTable.h @@ -0,0 +1,66 @@ +#ifndef GRAPHFUNCTIONTABLE_H +#define GRAPHFUNCTIONTABLE_H + +/* +Copyright (c) 1997-2025, 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 baseGraphStructure baseGraphStructure; + typedef baseGraphStructure *graphP; + + 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)(graphP); + void (*fpEmbedBackEdgeToDescendant)(graphP, int, int, int, int); + void (*fpWalkUp)(graphP, int, int); + int (*fpWalkDown)(graphP, int, int); + int (*fpMergeBicomps)(graphP, int, int, int, int); + void (*fpMergeVertex)(graphP, int, int, int); + int (*fpHandleInactiveVertex)(graphP, int, int *, int *); + int (*fpHandleBlockedBicomp)(graphP, int, int, int); + int (*fpEmbedPostprocess)(graphP, int, int); + int (*fpMarkDFSPath)(graphP, int, int); + + int (*fpCheckEmbeddingIntegrity)(graphP, graphP); + int (*fpCheckObstructionIntegrity)(graphP, graphP); + + // These function pointers allow extension modules to overload some + // of the behaviors of gp_* function in the public API + int (*fpInitGraph)(graphP, int); + void (*fpReinitializeGraph)(graphP); + int (*fpEnsureArcCapacity)(graphP, int); + int (*fpSortVertices)(graphP); + + int (*fpReadPostprocess)(graphP, char *); + int (*fpWritePostprocess)(graphP, char **); + + void (*fpHideEdge)(graphP, int); + void (*fpRestoreEdge)(graphP, int); + int (*fpHideVertex)(graphP, int); + int (*fpRestoreVertex)(graphP); + int (*fpContractEdge)(graphP, int); + int (*fpIdentifyVertices)(graphP, int, int, int); + + } graphFunctionTable; + + typedef graphFunctionTable *graphFunctionTableP; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/planarity_src/c/graphLib/graph.h b/planarity_src/c/graphLib/graph.h new file mode 100644 index 0000000..7ee07d7 --- /dev/null +++ b/planarity_src/c/graphLib/graph.h @@ -0,0 +1,143 @@ +#ifndef GRAPH_H +#define GRAPH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "graphStructures.h" + +#include "io/g6-read-iterator.h" +#include "io/g6-write-iterator.h" +#include "io/strbuf.h" +#include "io/strOrFile.h" + +#include "extensionSystem/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 const *FileName); + int gp_ReadFromString(graphP theGraph, char *inputStr); + +#define WRITE_ADJLIST 1 +#define WRITE_ADJMATRIX 2 +#define WRITE_DEBUGINFO 3 +#define WRITE_G6 4 + + int gp_Write(graphP theGraph, char const *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_DynamicAddEdge(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_RestoreVertex(graphP theGraph); + 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_LeastAncestor(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(const char *Line); + void _Log(const 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 diff --git a/planarity_src/c/graphLib/graphDFSUtils.c b/planarity_src/c/graphLib/graphDFSUtils.c new file mode 100644 index 0000000..cd0677e --- /dev/null +++ b/planarity_src/c/graphLib/graphDFSUtils.c @@ -0,0 +1,456 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define GRAPHDFSUTILS_C + +#include "graph.h" + +// Private methods, except exported within library +int _SortVertices(graphP theGraph); + +// Imported methods +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 "lowLevelUtils/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. + ********************************************************************/ + +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; +} diff --git a/planarity_src/c/graphLib/graphLib.h b/planarity_src/c/graphLib/graphLib.h new file mode 100644 index 0000000..dfd5daf --- /dev/null +++ b/planarity_src/c/graphLib/graphLib.h @@ -0,0 +1,59 @@ +#ifndef GRAPHLIB_H +#define GRAPHLIB_H + +/* +Copyright (c) 1997-2025, 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 "lowLevelUtils/platformTime.h" + +#include "homeomorphSearch/graphK23Search.h" +#include "homeomorphSearch/graphK33Search.h" +#include "homeomorphSearch/graphK4Search.h" +#include "planarityRelated/graphDrawPlanar.h" + + // This is the main location for the project and shared library version numbering. + // Changes here must be mirrored in configure.ac + // + // The overall project version numbering 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) + +#define GP_PROJECTVERSION_MAJOR 4 +#define GP_PROJECTVERSION_MINOR 0 +#define GP_PROJECTVERSION_MAINT 1 +#define GP_PROJECTVERSION_TWEAK 0 + + char *gp_GetProjectVersionFull(void); + +// Any change to the project version numbers should also affect the +// shared library version numbers below. +// +// See configure.ac for how to update these version numbers +#define GP_LIBPLANARITYVERSION_CURRENT 3 +#define GP_LIBPLANARITYVERSION_REVISION 0 +#define GP_LIBPLANARITYVERSION_AGE 1 + + char *gp_GetLibPlanarityVersionFull(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/planarity_src/c/graphLib/graphStructures.h b/planarity_src/c/graphLib/graphStructures.h new file mode 100644 index 0000000..9ccbbad --- /dev/null +++ b/planarity_src/c/graphLib/graphStructures.h @@ -0,0 +1,790 @@ +#ifndef GRAPHSTRUCTURE_H +#define GRAPHSTRUCTURE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "lowLevelUtils/appconst.h" +#include "lowLevelUtils/listcoll.h" +#include "lowLevelUtils/stack.h" + +#include "extensionSystem/graphExtensions.private.h" +#include "extensionSystem/graphFunctionTable.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; + +#ifdef USE_FASTER_1BASEDARRAYS +#define gp_IsArc(e) (e) +#define gp_IsNotArc(e) (!(e)) +#define gp_GetFirstEdge(theGraph) (2) +#else +#define gp_IsArc(e) ((e) != NIL) +#define gp_IsNotArc(e) ((e) == NIL) +#define gp_GetFirstEdge(theGraph) (0) +#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 +#ifdef USE_FASTER_1BASEDARRAYS +#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) +#else +#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) +#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 + */ + + struct baseGraphStructure + { + 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; + }; + + typedef struct baseGraphStructure baseGraphStructure; + typedef baseGraphStructure *graphP; + +#define gp_getN(theGraph) ((theGraph)->N) + + /* 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 between 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 preferable 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 diff --git a/planarity_src/c/graphLib/graphUtils.c b/planarity_src/c/graphLib/graphUtils.c new file mode 100644 index 0000000..3d44464 --- /dev/null +++ b/planarity_src/c/graphLib/graphUtils.c @@ -0,0 +1,2579 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "graphLib.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, char *extraData); +extern int _WritePostprocess(graphP theGraph, char **pExtraData); + +/* 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 _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); + +int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); +int _SetEdgeType(graphP theGraph, int u, int v); + +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); + +// Eliminates missingprototype warning +#ifndef DEBUG +int debugNOTOK(void); +#endif + +/******************************************************************** + Private functions. + ********************************************************************/ + +void _InitVertices(graphP theGraph); +void _InitEdges(graphP theGraph); + +void _ClearGraph(graphP theGraph); + +int _GetRandomNumber(int NMin, int NMax); + +int _getUnprocessedChild(graphP theGraph, int parent); +int _hasUnprocessedChild(graphP theGraph, int parent); + +void _RestoreArc(graphP theGraph, int arc); + +/* 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_GetProjectVersionFull() + Return full major.minor.maint.tweak version string for the graph planarity project + ********************************************************************/ + +char *gp_GetProjectVersionFull(void) +{ + static char projectVersionStr[MAXLINE + 1]; + sprintf(projectVersionStr, "%d.%d.%d.%d", + GP_PROJECTVERSION_MAJOR, + GP_PROJECTVERSION_MINOR, + GP_PROJECTVERSION_MAINT, + GP_PROJECTVERSION_TWEAK); + return projectVersionStr; +} + +/******************************************************************** + gp_GetLibPlanarityVersionFull() + Returns full current:revision:age version string for the graph planarity shared library + ********************************************************************/ + +char *gp_GetLibPlanarityVersionFull(void) +{ + static char libPlanarityVersionStr[MAXLINE + 1]; + sprintf(libPlanarityVersionStr, "%d:%d:%d", + GP_LIBPLANARITYVERSION_CURRENT, + GP_LIBPLANARITYVERSION_REVISION, + GP_LIBPLANARITYVERSION_AGE); + return libPlanarityVersionStr; +} + +/******************************************************************** + gp_New() + Constructor for graph object. + Can create two graphs if restricted to no dynamic memory. + ********************************************************************/ + +graphP gp_New(void) +{ + 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) +{ +#ifdef USE_FASTER_1BASEDARRAYS + 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)); +#else + 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); +#endif + // N.B. This is the legacy API-based approach to initializing the vertices + // 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); + // } +} + +/******************************************************************** + _InitEdges() + ********************************************************************/ +void _InitEdges(graphP theGraph) +{ +#ifdef USE_FASTER_1BASEDARRAYS + memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); +#else + 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); +#endif + // N.B. This is the legacy API-based approach to initializing the edges + // int e, Esize; + + // Esize = gp_EdgeIndexBound(theGraph); + // for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) + // _InitEdgeRec(theGraph, e); +} + +/******************************************************************** + 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_DynamicAddEdge() + Refer to documentation for gp_AddEdge for parameter description. + + Tries to call gp_AddEdge; if NONEMBEDDABLE, doubles the arc + capacity (up to a max of (N * (N-1))) using gp_EnsureArcCapacity, + then retries gp_AddEdge. + ********************************************************************/ +int gp_DynamicAddEdge(graphP theGraph, int u, int ulink, int v, int vlink) +{ + int Result = OK; + + if (theGraph == NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || + !gp_VirtualVertexInRange(theGraph, u) || !gp_VirtualVertexInRange(theGraph, v)) + return NOTOK; + + Result = gp_AddEdge(theGraph, u, ulink, v, vlink); + + if (Result == NONEMBEDDABLE) + { + int candidateArcCapacity = gp_GetArcCapacity(theGraph) * 2; + int N = theGraph->N; + int newArcCapacity = (candidateArcCapacity > (N * (N - 1))) ? (N * (N - 1)) : candidateArcCapacity; + Result = gp_EnsureArcCapacity(theGraph, newArcCapacity); + + if (Result != OK) + return Result; + + Result = gp_AddEdge(theGraph, u, ulink, v, vlink); + } + + return Result; +} + +/******************************************************************** + 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) +#ifdef USE_FASTER_1BASEDARRAYS + 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(void) +{ + // exit(-1); + return 0; // NOTOK is normally defined to be zero +} diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.c b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.c new file mode 100644 index 0000000..05dcd2a --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 1997-2025, 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) != OK || theGraph->IC.py == NIL) + 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) != OK || theGraph->IC.py == NIL) + 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); +} diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.h b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.h new file mode 100644 index 0000000..d7941a9 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_K23SEARCH_H +#define GRAPH_K23SEARCH_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.private.h b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.private.h new file mode 100644 index 0000000..3aee68c --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search.private.h @@ -0,0 +1,28 @@ +#ifndef GRAPH_K23SEARCH_PRIVATE_H +#define GRAPH_K23SEARCH_PRIVATE_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c new file mode 100644 index 0000000..00bf153 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c @@ -0,0 +1,262 @@ +/* +Copyright (c) 1997-2025, 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) +{ + int savedEmbedFlags = 0, savedZEROBASEDIO = 0; + + // For K2,3 search, we just return the edge embedding result because the + // search result has been obtained already. + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) + { + if (edgeEmbeddingResult == OK) + { + // When a graph does not contain a K2,3 homeomorph, the embedding + // is meaningless, so we empty it out. We preserve the embedFlags + // to ensure post-processing continues as expected. + savedEmbedFlags = theGraph->embedFlags; + savedZEROBASEDIO = theGraph->internalFlags & FLAGS_ZEROBASEDIO; + gp_ReinitializeGraph(theGraph); + theGraph->embedFlags = savedEmbedFlags; + theGraph->internalFlags &= savedZEROBASEDIO; + } + + 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; +} diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.c b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.c new file mode 100644 index 0000000..b49b408 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.c @@ -0,0 +1,2011 @@ +/* +Copyright (c) 1997-2025, 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 _MarkLowestXYPath(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); + +#ifndef USE_MERGEBLOCKER +extern int _IsolateMinorE2(graphP theGraph); +#endif + +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 _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 _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d); + +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. */ + + // Prior tests to choose the type of non-planarity minor selected the highest + // x-y path, so we need to clear the visited flags of that path before marking + // instead the x-y path with the lowest attachment points (those closest to W + // along the external face). + if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) + return NOTOK; + + // Now mark the lowest x-y path so that we can test whether _any_ x-y path + // has points of attachment, px or py, below x or y, respectively (where + // below means closer to W than x or y, respectively, along the external face). + if (_MarkLowestXYPath(theGraph) != TRUE) + return NOTOK; + + // Now we test for E4 based on whether px!=x or py!=y. Note that the inequality + // test is sufficient because not equal means attached lower by the time we are + // testing for E4 because the prior test for non-planarity minor C already ruled + // out the possibility that inequality could mean attached higher than x or y. + 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. */ + + // Since the E4 test above has already marked the lowest X-Y path, and only + // the lowest one could possibly have a Z-to-W path attached to it, we + // simply reuse the x-y path from E4 here in the E5 test. + // (NOTE: Only the lowest X-Y path could have a Z-to-W path because all + // bicomps are planar embeddings, and so a Z-to-W path emanating + // from a higher X-Y path would cross the lowest one, violating the + // planarity of the bicomp). + 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; +} + +/**************************************************************************** + _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: The X-Y path is marked visited by a prior invocation of + the meothd _MarkLowestXYPath() above. + + 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 connect 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; +} diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.h b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.h new file mode 100644 index 0000000..074a076 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_K33SEARCH_H +#define GRAPH_K33SEARCH_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.private.h b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.private.h new file mode 100644 index 0000000..36c36ff --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search.private.h @@ -0,0 +1,62 @@ +#ifndef GRAPH_K33SEARCH_PRIVATE_H +#define GRAPH_K33SEARCH_PRIVATE_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c new file mode 100644 index 0000000..d449dd8 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c @@ -0,0 +1,766 @@ +/* +Copyright (c) 1997-2025, 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) +{ + memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(K33Search_VertexInfo)); + memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K33Search_EdgeRec)); + // N.B. This is the legacy API-based approach to initializing the structures + // required for the K_{3, 3} search graph algorithm extension. + // 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); + + 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); + } + + // No way to get here in current implementation, but this protects against future mistakes + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + int savedEmbedFlags = 0, savedZEROBASEDIO = 0; + + // For K3,3 search, we just return the edge embedding result because the + // search result has been obtained already. + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + if (edgeEmbeddingResult == OK) + { + // When a graph does not contain a K3,3 homeomorph, the embedding + // is meaningless, so we empty it out. We preserve the embedFlags + // to ensure post-processing continues as expected. + savedEmbedFlags = theGraph->embedFlags; + savedZEROBASEDIO = theGraph->internalFlags & FLAGS_ZEROBASEDIO; + gp_ReinitializeGraph(theGraph); + theGraph->embedFlags = savedEmbedFlags; + theGraph->internalFlags &= savedZEROBASEDIO; + } + + 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; +} diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.c b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.c new file mode 100644 index 0000000..9364fcf --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.c @@ -0,0 +1,1491 @@ +/* +Copyright (c) 1997-2025, 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_DeleteEdge(graphP theGraph, K4SearchContext *context, int e, int nextLink); + +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; + + // Now Mark the X-Y path + // 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) != OK) + return NOTOK; + + // If there was an X-Y path to mark... + if (theGraph->IC.py != NIL) + { + // 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; + } + + // Fail if there is an internal error or if there isn't an X-Y path + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + + // Isolate the K4 homeomorph + if (_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 (but it has to be there, else error). + if (_SetVertexTypesForMarkingXYPath(theGraph) != OK || + _MarkHighestXYPath(theGraph) != OK || + theGraph->IC.py == NIL) + 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) != OK || theGraph->IC.py == NIL) + 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; +} + +/******************************************************************** + _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; +} + +/******************************************************************** + 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_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; +} diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.h b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.h new file mode 100644 index 0000000..02f7409 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_K4SEARCH_H +#define GRAPH_K4SEARCH_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.private.h b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.private.h new file mode 100644 index 0000000..73c1de8 --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search.private.h @@ -0,0 +1,61 @@ +#ifndef GRAPH_K4SEARCH_PRIVATE_H +#define GRAPH_K4SEARCH_PRIVATE_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c new file mode 100644 index 0000000..4f10a1a --- /dev/null +++ b/planarity_src/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c @@ -0,0 +1,510 @@ +/* +Copyright (c) 1997-2025, 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) +{ + memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K4Search_EdgeRec)); + // N.B. This is the legacy API-based approach to initializing the structures + // required for the K_4 search graph algorithm extension. + // int e, Esize; + + // Esize = gp_EdgeIndexBound(context->theGraph); + // for (e = gp_GetFirstEdge(context->theGraph); e < Esize; e++) + // _K4Search_InitEdgeRec(context, e); + + 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); + } + + // No way to get here in current implementation, but this protects against future mistakes + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + int savedEmbedFlags = 0, savedZEROBASEDIO = 0; + + // For K4 search, we just return the edge embedding result because the + // search result has been obtained already. + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) + { + if (edgeEmbeddingResult == OK) + { + // When a graph does not contain a K4 homeomorph, the embedding + // is meaningless, so we empty it out. We preserve the embedFlags + // to ensure post-processing continues as expected. + savedEmbedFlags = theGraph->embedFlags; + savedZEROBASEDIO = theGraph->internalFlags & FLAGS_ZEROBASEDIO; + gp_ReinitializeGraph(theGraph); + theGraph->embedFlags = savedEmbedFlags; + theGraph->internalFlags &= savedZEROBASEDIO; + } + + 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; +} diff --git a/planarity_src/c/graphLib/io/g6-api-utilities.c b/planarity_src/c/graphLib/io/g6-api-utilities.c new file mode 100644 index 0000000..39195e1 --- /dev/null +++ b/planarity_src/c/graphLib/io/g6-api-utilities.c @@ -0,0 +1,41 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "g6-api-utilities.h" + +int _getMaxEdgeCount(int graphOrder) +{ + return (graphOrder * (graphOrder - 1)) / 2; +} + +int _getNumCharsForGraphEncoding(int graphOrder) +{ + int maxNumEdges = _getMaxEdgeCount(graphOrder); + + return (maxNumEdges / 6) + (maxNumEdges % 6 ? 1 : 0); +} + +int _getNumCharsForGraphOrder(int graphOrder) +{ + if (graphOrder > 0 && graphOrder < 63) + { + return 1; + } + else if (graphOrder >= 63 && graphOrder <= 100000) + { + return 4; + } + + return -1; +} + +int _getExpectedNumPaddingZeroes(const int graphOrder, const int numChars) +{ + int maxNumEdges = _getMaxEdgeCount(graphOrder); + int expectedNumPaddingZeroes = numChars * 6 - maxNumEdges; + + return expectedNumPaddingZeroes; +} diff --git a/planarity_src/c/graphLib/io/g6-api-utilities.h b/planarity_src/c/graphLib/io/g6-api-utilities.h new file mode 100644 index 0000000..15239d9 --- /dev/null +++ b/planarity_src/c/graphLib/io/g6-api-utilities.h @@ -0,0 +1,24 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef G6_API_UTILITIES +#define G6_API_UTILITIES + +#ifdef __cplusplus +extern "C" +{ +#endif + + int _getMaxEdgeCount(int); + int _getNumCharsForGraphEncoding(int); + int _getNumCharsForGraphOrder(int); + int _getExpectedNumPaddingZeroes(const int, const int); + +#ifdef __cplusplus +} +#endif + +#endif /* G6_API_UTILITIES */ diff --git a/planarity_src/c/graphLib/io/g6-read-iterator.c b/planarity_src/c/graphLib/io/g6-read-iterator.c new file mode 100644 index 0000000..c5c5397 --- /dev/null +++ b/planarity_src/c/graphLib/io/g6-read-iterator.c @@ -0,0 +1,783 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include + +#include "g6-read-iterator.h" +#include "g6-api-utilities.h" + +int allocateG6ReadIterator(G6ReadIteratorP *ppG6ReadIterator, graphP pGraph) +{ + int exitCode = OK; + + if (ppG6ReadIterator != NULL && (*ppG6ReadIterator) != NULL) + { + ErrorMessage("G6ReadIterator is not NULL and therefore can't be allocated.\n"); + return NOTOK; + } + + // numGraphsRead, graphOrder, numCharsForGraphOrder, + // numCharsForGraphEncoding, and currGraphBuffSize all set to 0 + (*ppG6ReadIterator) = (G6ReadIteratorP)calloc(1, sizeof(G6ReadIterator)); + + if ((*ppG6ReadIterator) == NULL) + { + ErrorMessage("Unable to allocate memory for G6ReadIterator.\n"); + return NOTOK; + } + + (*ppG6ReadIterator)->g6Input = NULL; + + if (pGraph == NULL) + { + ErrorMessage("Must allocate graph to be used by G6ReadIterator.\n"); + + exitCode = freeG6ReadIterator(ppG6ReadIterator); + + if (exitCode != OK) + ErrorMessage("Unable to free the G6ReadIterator.\n"); + } + else + { + (*ppG6ReadIterator)->currGraph = pGraph; + } + + return exitCode; +} + +bool _isG6ReadIteratorAllocated(G6ReadIteratorP pG6ReadIterator) +{ + bool g6ReadIteratorIsAllocated = true; + + if (pG6ReadIterator == NULL) + { + ErrorMessage("G6ReadIterator is NULL.\n"); + g6ReadIteratorIsAllocated = false; + } + else + { + if (sf_ValidateStrOrFile(pG6ReadIterator->g6Input) != OK) + { + ErrorMessage("G6ReadIterator's g6Input string-or-file container " + "is not valid.\n"); + g6ReadIteratorIsAllocated = false; + } + if (pG6ReadIterator->currGraphBuff == NULL) + { + ErrorMessage("G6ReadIterator's currGraphBuff is NULL.\n"); + g6ReadIteratorIsAllocated = false; + } + if (pG6ReadIterator->currGraph == NULL) + { + ErrorMessage("G6ReadIterator's currGraph is NULL.\n"); + g6ReadIteratorIsAllocated = false; + } + } + + return g6ReadIteratorIsAllocated; +} + +bool contentsExhausted(G6ReadIteratorP pG6ReadIterator) +{ + if (pG6ReadIterator == NULL || + pG6ReadIterator->currGraph == NULL || + pG6ReadIterator->contentsExhausted) + { + return true; + } + return false; +} + +int getNumGraphsRead(G6ReadIteratorP pG6ReadIterator, int *pNumGraphsRead) +{ + if (_isG6ReadIteratorAllocated(pG6ReadIterator) == false) + { + ErrorMessage("Unable to get numGraphsRead, as G6ReadIterator is not " + "allocated.\n"); + (*pNumGraphsRead) = 0; + return NOTOK; + } + + (*pNumGraphsRead) = pG6ReadIterator->numGraphsRead; + + return OK; +} + +int getOrderOfGraphToRead(G6ReadIteratorP pG6ReadIterator, int *pGraphOrder) +{ + if (pG6ReadIterator == NULL) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + (*pGraphOrder) = pG6ReadIterator->graphOrder; + + return OK; +} + +int getPointerToGraphReadIn(G6ReadIteratorP pG6ReadIterator, graphP *ppGraph) +{ + if (pG6ReadIterator == NULL) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + (*ppGraph) = pG6ReadIterator->currGraph; + + return OK; +} + +int beginG6ReadIterationFromG6String(G6ReadIteratorP pG6ReadIterator, char *inputString) +{ + return beginG6ReadIterationFromG6StrOrFile( + pG6ReadIterator, + sf_New(inputString, NULL, READTEXT)); +} + +int beginG6ReadIterationFromG6FilePath(G6ReadIteratorP pG6ReadIterator, char *infileName) +{ + return beginG6ReadIterationFromG6StrOrFile( + pG6ReadIterator, + sf_New(NULL, infileName, READTEXT)); +} + +int beginG6ReadIterationFromG6StrOrFile(G6ReadIteratorP pG6ReadIterator, strOrFileP g6InputContainer) +{ + if ( + sf_ValidateStrOrFile(g6InputContainer) != OK || + (g6InputContainer->theStr != NULL && sb_GetSize(g6InputContainer->theStr) == 0)) + { + ErrorMessage("Invalid g6InputContainer; must contain either valid input stream or non-empty string.\n"); + return NOTOK; + } + + pG6ReadIterator->g6Input = g6InputContainer; + + return _beginG6ReadIteration(pG6ReadIterator); +} + +int _beginG6ReadIteration(G6ReadIteratorP pG6ReadIterator) +{ + int exitCode = OK; + char charConfirmation = EOF; + int firstChar = '\0'; + int lineNum = 1; + int graphOrder = -1; + strOrFileP g6Input = pG6ReadIterator->g6Input; + char messageContents[MAXLINE + 1]; + messageContents[0] = '\0'; + + if ((firstChar = sf_getc(g6Input)) == EOF) + { + ErrorMessage(".g6 infile is empty.\n"); + return NOTOK; + } + else + { + charConfirmation = sf_ungetc((char)firstChar, g6Input); + + if (charConfirmation != firstChar) + { + ErrorMessage("Unable to ungetc first character.\n"); + return NOTOK; + } + + if (firstChar == '>') + { + exitCode = _processAndCheckHeader(g6Input); + if (exitCode != OK) + { + ErrorMessage("Unable to process and check .g6 infile header.\n"); + return exitCode; + } + } + } + + firstChar = sf_getc(g6Input); + charConfirmation = sf_ungetc((char)firstChar, g6Input); + + if (charConfirmation != firstChar) + { + ErrorMessage("Unable to ungetc first character.\n"); + return NOTOK; + } + + if (!_firstCharIsValid((char)firstChar, lineNum)) + return NOTOK; + + // Despite the general specification indicating that n \in [0, 68,719,476,735], + // in practice n will be limited such that an integer will suffice in storing it. + exitCode = _getGraphOrder(g6Input, &graphOrder); + + if (exitCode != OK) + { + sprintf(messageContents, "Invalid graph order on line %d of .g6 file.\n", lineNum); + ErrorMessage(messageContents); + return exitCode; + } + + if (gp_getN(pG6ReadIterator->currGraph) == 0) + { + exitCode = gp_InitGraph(pG6ReadIterator->currGraph, graphOrder); + + if (exitCode != OK) + { + sprintf(messageContents, "Unable to initialize graph datastructure with order %d for graph on line %d of the .g6 file.\n", graphOrder, lineNum); + ErrorMessage(messageContents); + return exitCode; + } + + pG6ReadIterator->graphOrder = graphOrder; + } + else + { + if (gp_getN(pG6ReadIterator->currGraph) != graphOrder) + { + sprintf(messageContents, "Graph datastructure passed to G6ReadIterator already initialized with graph order %d,\n", gp_getN(pG6ReadIterator->currGraph)); + ErrorMessage(messageContents); + sprintf(messageContents, "\twhich doesn't match the graph order %d specified in the file.\n", graphOrder); + ErrorMessage(messageContents); + return NOTOK; + } + else + { + gp_ReinitializeGraph(pG6ReadIterator->currGraph); + pG6ReadIterator->graphOrder = graphOrder; + } + } + + // Ensures zero-based flag is set regardless of whether the graph was initialized or reinitialized. + pG6ReadIterator->currGraph->internalFlags |= FLAGS_ZEROBASEDIO; + + pG6ReadIterator->numCharsForGraphOrder = _getNumCharsForGraphOrder(graphOrder); + pG6ReadIterator->numCharsForGraphEncoding = _getNumCharsForGraphEncoding(graphOrder); + // Must add 3 bytes for newline, possible carriage return, and null terminator + pG6ReadIterator->currGraphBuffSize = pG6ReadIterator->numCharsForGraphOrder + pG6ReadIterator->numCharsForGraphEncoding + 3; + pG6ReadIterator->currGraphBuff = (char *)calloc(pG6ReadIterator->currGraphBuffSize, sizeof(char)); + + if (pG6ReadIterator->currGraphBuff == NULL) + { + ErrorMessage("Unable to allocate memory for currGraphBuff.\n"); + exitCode = NOTOK; + } + + return exitCode; +} + +int _processAndCheckHeader(strOrFileP g6Input) +{ + int exitCode = OK; + char const *correctG6Header = ">>graph6<<"; + char const *sparse6Header = ">>sparse6<"; + char const *digraph6Header = ">>digraph6"; + char headerCandidateChars[11]; + headerCandidateChars[0] = '\0'; + + if (g6Input == NULL) + { + ErrorMessage("Invalid .g6 string-or-file container.\n"); + return NOTOK; + } + + for (int i = 0; i < 10; i++) + { + headerCandidateChars[i] = sf_getc(g6Input); + } + + headerCandidateChars[10] = '\0'; + + if (strcmp(correctG6Header, headerCandidateChars) != 0) + { + if (strcmp(sparse6Header, headerCandidateChars) == 0) + ErrorMessage("Graph file is sparse6 format, which is not supported.\n"); + else if (strcmp(digraph6Header, headerCandidateChars) == 0) + ErrorMessage("Graph file is digraph6 format, which is not supported.\n"); + else + ErrorMessage("Invalid header for .g6 file.\n"); + + exitCode = NOTOK; + } + + return exitCode; +} + +bool _firstCharIsValid(char c, const int lineNum) +{ + bool isValidFirstChar = false; + + if (strchr(":;&", c) != NULL) + { + char messageContents[MAXLINE + 1]; + sprintf(messageContents, "Invalid first character on line %d, i.e. one of ':', ';', or '&'; aborting.\n", lineNum); + ErrorMessage(messageContents); + } + else + isValidFirstChar = true; + + return isValidFirstChar; +} + +int _getGraphOrder(strOrFileP g6Input, int *graphOrder) +{ + int exitCode = OK; + int n = 0; + int graphChar = '\0'; + + if (g6Input == NULL) + { + ErrorMessage("Invalid string-or-file container for .g6 input.\n"); + return NOTOK; + } + + // Since geng: n must be in the range 1..32, and since edge-addition-planarity-suite + // processing of random graphs may only handle up to n = 100,000, we will only check + // if 1 or 4 bytes are necessary + if ((graphChar = sf_getc(g6Input)) == 126) + { + if ((graphChar = sf_getc(g6Input)) == 126) + { + ErrorMessage("Graph order is too large; format suggests that 258048 <= n <= 68719476735, but we only support n <= 100000.\n"); + return NOTOK; + } + + sf_ungetc((char)graphChar, g6Input); + + for (int i = 2; i >= 0; i--) + { + graphChar = sf_getc(g6Input) - 63; + n |= graphChar << (6 * i); + } + + if (n > 100000) + { + ErrorMessage("Graph order is too large; we only support n <= 100000.\n"); + return NOTOK; + } + } + else if (graphChar > 62 && graphChar < 126) + n = graphChar - 63; + else + { + ErrorMessage("Graph order is too small; character doesn't correspond to a printable ASCII character.\n"); + return NOTOK; + } + + (*graphOrder) = n; + + return exitCode; +} + +int readGraphUsingG6ReadIterator(G6ReadIteratorP pG6ReadIterator) +{ + int exitCode = OK; + strOrFileP g6Input = NULL; + int numGraphsRead = 0; + char *currGraphBuff = NULL; + char firstChar = '\0'; + char *graphEncodingChars = NULL; + graphP currGraph = NULL; + const int graphOrder = pG6ReadIterator == NULL ? 0 : pG6ReadIterator->graphOrder; + const int numCharsForGraphOrder = pG6ReadIterator == NULL ? 0 : pG6ReadIterator->numCharsForGraphOrder; + const int numCharsForGraphEncoding = pG6ReadIterator == NULL ? 0 : pG6ReadIterator->numCharsForGraphEncoding; + const int currGraphBuffSize = pG6ReadIterator == NULL ? 0 : pG6ReadIterator->currGraphBuffSize; + char messageContents[MAXLINE + 1]; + messageContents[0] = '\0'; + + if (!_isG6ReadIteratorAllocated(pG6ReadIterator)) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + if ((g6Input = pG6ReadIterator->g6Input) == NULL) + { + ErrorMessage("Pointer to .g6 string-or-file container is NULL.\n"); + return NOTOK; + } + + numGraphsRead = pG6ReadIterator->numGraphsRead; + + if ((currGraphBuff = pG6ReadIterator->currGraphBuff) == NULL) + { + ErrorMessage("currGraphBuff string is null.\n"); + return NOTOK; + } + + currGraph = pG6ReadIterator->currGraph; + + if (sf_fgets(currGraphBuff, currGraphBuffSize, g6Input) != NULL) + { + numGraphsRead++; + firstChar = currGraphBuff[0]; + + if (!_firstCharIsValid(firstChar, numGraphsRead)) + return NOTOK; + + // From https://stackoverflow.com/a/28462221, strcspn finds the index of the first + // char in charset; this way, I replace the char at that index with the null-terminator + currGraphBuff[strcspn(currGraphBuff, "\n\r")] = '\0'; // works for LF, CR, CRLF, LFCR, ... + + // If the line was too long, then we would have placed the null terminator at the final + // index (where it already was; see strcpn docs), and the length of the string will be + // longer than the line should have been, i.e. orderOffset + numCharsForGraphRepr + if ((int)strlen(currGraphBuff) != (((numGraphsRead == 1) ? 0 : numCharsForGraphOrder) + numCharsForGraphEncoding)) + { + sprintf(messageContents, "Invalid line length read on line %d\n", numGraphsRead); + ErrorMessage(messageContents); + return NOTOK; + } + + if (numGraphsRead > 1) + { + exitCode = _checkGraphOrder(currGraphBuff, graphOrder); + + if (exitCode != OK) + { + sprintf(messageContents, "Order of graph on line %d is incorrect.\n", numGraphsRead); + ErrorMessage(messageContents); + return exitCode; + } + } + + // On first line, we have already processed the characters corresponding to the graph + // order, so there's no need to apply the offset. On subsequent lines, the orderOffset + // must be applied so that we are only starting validation on the byte corresponding to + // the encoding of the adjacency matrix. + graphEncodingChars = (numGraphsRead == 1) ? currGraphBuff : currGraphBuff + numCharsForGraphOrder; + + exitCode = _validateGraphEncoding(graphEncodingChars, graphOrder, numCharsForGraphEncoding); + + if (exitCode != OK) + { + sprintf(messageContents, "Graph on line %d is invalid.", numGraphsRead); + ErrorMessage(messageContents); + return exitCode; + } + + if (numGraphsRead > 1) + { + gp_ReinitializeGraph(currGraph); + // Ensures zero-based flag is set after reinitializing graph. + currGraph->internalFlags |= FLAGS_ZEROBASEDIO; + } + + exitCode = _decodeGraph(graphEncodingChars, graphOrder, numCharsForGraphEncoding, currGraph); + + if (exitCode != OK) + { + sprintf(messageContents, "Unable to interpret bits on line %d to populate adjacency matrix.\n", numGraphsRead); + ErrorMessage(messageContents); + return exitCode; + } + + pG6ReadIterator->numGraphsRead = numGraphsRead; + } + else + { + pG6ReadIterator->currGraph = NULL; + pG6ReadIterator->contentsExhausted = true; + } + + return exitCode; +} + +int _checkGraphOrder(char *graphBuff, int graphOrder) +{ + int exitCode = OK; + int n = 0; + char currChar = graphBuff[0]; + + if (currChar == 126) + { + if (graphBuff[1] == 126) + { + ErrorMessage("Can only handle graphs of order <= 100,000.\n"); + return NOTOK; + } + else if (graphBuff[1] > 126) + { + ErrorMessage("Invalid graph order signifier.\n"); + return NOTOK; + } + else + { + int orderCharIndex = 2; + for (int i = 1; i < 4; i++) + n |= (graphBuff[i] - 63) << (6 * orderCharIndex--); + } + } + else if (currChar > 62 && currChar < 126) + n = currChar - 63; + else + { + ErrorMessage("Character doesn't correspond to a printable ASCII character.\n"); + return NOTOK; + } + + if (n != graphOrder) + { + char messageContents[MAXLINE + 1]; + sprintf(messageContents, "Graph order %d doesn't match expected graph order %d", n, graphOrder); + ErrorMessage(messageContents); + exitCode = NOTOK; + } + + return exitCode; +} + +int _validateGraphEncoding(char *graphBuff, const int graphOrder, const int numChars) +{ + int exitCode = OK; + int expectedNumPaddingZeroes = _getExpectedNumPaddingZeroes(graphOrder, numChars); + char finalByte = graphBuff[numChars - 1] - 63; + int numPaddingZeroes = 0; + + // Num edges of the graph (and therefore the number of bits) is (n * (n-1))/2, and + // since each resulting byte needs to correspond to an ascii character between 63 and 126, + // each group is only comprised of 6 bits (to which we add 63 for the final byte value) + int expectedNumChars = _getNumCharsForGraphEncoding(graphOrder); + int numCharsForGraphEncoding = strlen(graphBuff); + + if (expectedNumChars != numCharsForGraphEncoding) + { + char messageContents[MAXLINE + 1]; + messageContents[0] = '\0'; + sprintf(messageContents, "Invalid number of bytes for graph of order %d; got %d but expected %d\n", graphOrder, numCharsForGraphEncoding, expectedNumChars); + ErrorMessage(messageContents); + return NOTOK; + } + + // Check that characters are valid ASCII characters between 62 and 126 + for (int i = 0; i < numChars; i++) + { + if (graphBuff[i] < 63 || graphBuff[i] > 126) + { + char messageContents[MAXLINE + 1]; + messageContents[0] = '\0'; + sprintf(messageContents, "Invalid character at index %d: \"%c\"\n", i, graphBuff[i]); + ErrorMessage(messageContents); + return NOTOK; + } + } + + // Check that there are no extraneous bits in representation (since we pad out to a + // multiple of 6 before splitting into bytes and adding 63 to each byte) + for (int i = 0; i < expectedNumPaddingZeroes; i++) + { + if (finalByte & (1 << i)) + break; + + numPaddingZeroes++; + } + + if (numPaddingZeroes != expectedNumPaddingZeroes) + { + char messageContents[MAXLINE + 1]; + messageContents[0] = '\0'; + sprintf(messageContents, "Expected %d padding zeroes, but got %d.\n", expectedNumPaddingZeroes, numPaddingZeroes); + ErrorMessage(messageContents); + exitCode = NOTOK; + } + + return exitCode; +} + +// Takes the character array graphBuff, the derived number of vertices graphOrder, +// and the numChars corresponding to the number of characters after the first byte +// and performs the inverse transformation of the graph encoding: we subtract 63 from +// each byte, then only process the 6 least significant bits of the resulting byte. For +// the final byte, we determine how many padding zeroes to expect, and exclude them +// from being processed. We index into the adjacency matrix by row and column, which +// are incremented such that row ranges from 0 to one less than the column index. +int _decodeGraph(char *graphBuff, const int graphOrder, const int numChars, graphP pGraph) +{ + int exitCode = OK; + + int numPaddingZeroes = _getExpectedNumPaddingZeroes(graphOrder, numChars); + + char currByte = '\0'; + int bitValue = 0; + int row = 0; + int col = 1; + + if (pGraph == NULL) + { + ErrorMessage("Must initialize graph datastructure before invoking _decodeGraph.\n"); + return NOTOK; + } + + for (int i = 0; i < numChars; i++) + { + currByte = graphBuff[i] - 63; + // j corresponds to the number of places one must bitshift the byte by to read + // the next bit in the byte + for (int j = sizeof(char) * 5; j >= 0; j--) + { + // If we are on the final byte, we know that the final numPaddingZeroes bits + // can be ignored, so we break out of the loop + if ((i == numChars) && j == numPaddingZeroes - 1) + break; + + if (row == col) + { + row = 0; + col++; + } + + bitValue = ((currByte >> j) & 1u) ? 1 : 0; + if (bitValue == 1) + { + // Add gp_GetFirstVertex(pGraph), which is 1 if NIL == 0 (i.e. internal 1-based labelling) and 0 if NIL == -1 (internally 0-based) + exitCode = gp_DynamicAddEdge(pGraph, row + gp_GetFirstVertex(pGraph), 0, col + gp_GetFirstVertex(pGraph), 0); + if (exitCode != OK) + return exitCode; + } + + row++; + } + } + + return exitCode; +} + +int endG6ReadIteration(G6ReadIteratorP pG6ReadIterator) +{ + int exitCode = OK; + + if (pG6ReadIterator != NULL) + { + if (pG6ReadIterator->g6Input != NULL) + sf_Free(&(pG6ReadIterator->g6Input)); + + if (pG6ReadIterator->currGraphBuff != NULL) + { + free(pG6ReadIterator->currGraphBuff); + pG6ReadIterator->currGraphBuff = NULL; + } + } + + return exitCode; +} + +int freeG6ReadIterator(G6ReadIteratorP *ppG6ReadIterator) +{ + int exitCode = OK; + + if (ppG6ReadIterator != NULL && (*ppG6ReadIterator) != NULL) + { + if ((*ppG6ReadIterator)->g6Input != NULL) + sf_Free(&((*ppG6ReadIterator)->g6Input)); + + (*ppG6ReadIterator)->numGraphsRead = 0; + (*ppG6ReadIterator)->graphOrder = 0; + + if ((*ppG6ReadIterator)->currGraphBuff != NULL) + { + free((*ppG6ReadIterator)->currGraphBuff); + (*ppG6ReadIterator)->currGraphBuff = NULL; + } + + (*ppG6ReadIterator)->currGraph = NULL; + + free((*ppG6ReadIterator)); + (*ppG6ReadIterator) = NULL; + } + + return exitCode; +} + +int _ReadGraphFromG6FilePath(graphP pGraphToRead, char *pathToG6File) +{ + char const *messageFormat = NULL; + int charsAvailForStr = 0; + + strOrFileP inputContainer = sf_New(NULL, pathToG6File, READTEXT); + if (inputContainer == NULL) + { + char messageContents[MAXLINE + 1]; + messageContents[0] = '\0'; + messageFormat = "Unable to allocate strOrFile container for infile \"%.*s\".\n"; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(messageContents, messageFormat, charsAvailForStr, pathToG6File); +#pragma GCC diagnostic pop + ErrorMessage(messageContents); + + return NOTOK; + } + + return _ReadGraphFromG6StrOrFile(pGraphToRead, inputContainer); +} + +int _ReadGraphFromG6String(graphP pGraphToRead, char *g6EncodedString) +{ + strOrFileP inputContainer = NULL; + + if (g6EncodedString == NULL || strlen(g6EncodedString) == 0) + { + ErrorMessage("Unable to proceed with empty .g6 input string.\n"); + return NOTOK; + } + + if ((inputContainer = sf_New(g6EncodedString, NULL, READTEXT)) == NULL) + { + ErrorMessage("Unable to allocate strOrFile container for .g6 input string.\n"); + return NOTOK; + } + + return _ReadGraphFromG6StrOrFile(pGraphToRead, inputContainer); +} + +int _ReadGraphFromG6StrOrFile(graphP pGraphToRead, strOrFileP g6InputContainer) +{ + int exitCode = OK; + + G6ReadIteratorP pG6ReadIterator = NULL; + + if (sf_ValidateStrOrFile(g6InputContainer) != OK) + { + ErrorMessage("Invalid G6 output container.\n"); + return NOTOK; + } + + if (allocateG6ReadIterator(&pG6ReadIterator, pGraphToRead) != OK) + { + ErrorMessage("Unable to allocate G6ReadIterator.\n"); + return NOTOK; + } + + if (beginG6ReadIterationFromG6StrOrFile(pG6ReadIterator, g6InputContainer) != OK) + { + ErrorMessage("Unable to begin .g6 read iteration.\n"); + + if (freeG6ReadIterator(&pG6ReadIterator) != OK) + ErrorMessage("Unable to free G6ReadIterator.\n"); + + return NOTOK; + } + + exitCode = readGraphUsingG6ReadIterator(pG6ReadIterator); + if (exitCode != OK) + ErrorMessage("Unable to read graph from .g6 read iterator.\n"); + + if (endG6ReadIteration(pG6ReadIterator) != OK) + { + ErrorMessage("Unable to end G6ReadIterator.\n"); + exitCode = NOTOK; + } + + if (freeG6ReadIterator(&pG6ReadIterator) != OK) + { + ErrorMessage("Unable to free G6ReadIterator.\n"); + exitCode = NOTOK; + } + + return exitCode; +} diff --git a/planarity_src/c/graphLib/io/g6-read-iterator.h b/planarity_src/c/graphLib/io/g6-read-iterator.h new file mode 100644 index 0000000..76f3223 --- /dev/null +++ b/planarity_src/c/graphLib/io/g6-read-iterator.h @@ -0,0 +1,71 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef G6_READ_ITERATOR +#define G6_READ_ITERATOR + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "../graph.h" +#include "strOrFile.h" + + typedef struct + { + strOrFileP g6Input; + int numGraphsRead; + + int graphOrder; + int numCharsForGraphOrder; + int numCharsForGraphEncoding; + int currGraphBuffSize; + char *currGraphBuff; + + graphP currGraph; + + bool contentsExhausted; + } G6ReadIterator; + typedef G6ReadIterator *G6ReadIteratorP; + + int allocateG6ReadIterator(G6ReadIteratorP *, graphP); + bool _isG6ReadIteratorAllocated(G6ReadIteratorP); + bool contentsExhausted(G6ReadIteratorP pG6ReadIterator); + + int getNumGraphsRead(G6ReadIteratorP, int *); + int getOrderOfGraphToRead(G6ReadIteratorP, int *); + int getPointerToGraphReadIn(G6ReadIteratorP, graphP *); + + int beginG6ReadIterationFromG6String(G6ReadIteratorP, char *); + int beginG6ReadIterationFromG6FilePath(G6ReadIteratorP, char *); + int beginG6ReadIterationFromG6StrOrFile(G6ReadIteratorP, strOrFileP); + int _beginG6ReadIteration(G6ReadIteratorP); + int _processAndCheckHeader(strOrFileP); + bool _firstCharIsValid(char, const int); + int _getGraphOrder(strOrFileP, int *); + + int readGraphUsingG6ReadIterator(G6ReadIteratorP); + int _checkGraphOrder(char *, int); + int _validateGraphEncoding(char *, const int, const int); + int _decodeGraph(char *, const int, const int, graphP); + + int endG6ReadIteration(G6ReadIteratorP); + + int freeG6ReadIterator(G6ReadIteratorP *); + + int _ReadGraphFromG6FilePath(graphP, char *); + int _ReadGraphFromG6String(graphP, char *); + int _ReadGraphFromG6StrOrFile(graphP, strOrFileP); + +#ifdef __cplusplus +} +#endif + +#endif /* G6_READ_ITERATOR */ diff --git a/planarity_src/c/graphLib/io/g6-write-iterator.c b/planarity_src/c/graphLib/io/g6-write-iterator.c new file mode 100644 index 0000000..6aea9d8 --- /dev/null +++ b/planarity_src/c/graphLib/io/g6-write-iterator.c @@ -0,0 +1,594 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include + +#include "g6-write-iterator.h" +#include "g6-api-utilities.h" + +int allocateG6WriteIterator(G6WriteIteratorP *ppG6WriteIterator, graphP pGraph) +{ + int exitCode = OK; + + if (ppG6WriteIterator != NULL && (*ppG6WriteIterator) != NULL) + { + ErrorMessage("G6WriteIterator is not NULL and therefore can't be allocated.\n"); + return NOTOK; + } + + // numGraphsWritten, graphOrder, numCharsForGraphOrder, + // numCharsForGraphEncoding, and currGraphBuffSize all set to 0 + (*ppG6WriteIterator) = (G6WriteIteratorP)calloc(1, sizeof(G6WriteIterator)); + + if ((*ppG6WriteIterator) == NULL) + { + ErrorMessage("Unable to allocate memory for G6WriteIterator.\n"); + return NOTOK; + } + + (*ppG6WriteIterator)->g6Output = NULL; + (*ppG6WriteIterator)->currGraphBuff = NULL; + (*ppG6WriteIterator)->columnOffsets = NULL; + + if (pGraph == NULL || gp_getN(pGraph) <= 0) + { + ErrorMessage("[ERROR] Must allocate and initialize graph with an order greater than 0 to use the G6WriteIterator.\n"); + + exitCode = freeG6WriteIterator(ppG6WriteIterator); + + if (exitCode != OK) + ErrorMessage("Unable to free the G6WriteIterator.\n"); + } + else + (*ppG6WriteIterator)->currGraph = pGraph; + + return exitCode; +} + +bool _isG6WriteIteratorAllocated(G6WriteIteratorP pG6WriteIterator) +{ + bool G6WriteIteratorIsAllocated = true; + + if (pG6WriteIterator == NULL) + { + ErrorMessage("G6WriteIterator is NULL.\n"); + G6WriteIteratorIsAllocated = false; + } + else + { + if (sf_ValidateStrOrFile(pG6WriteIterator->g6Output) != OK) + { + ErrorMessage("G6WriteIterator's g6Output is not valid.\n"); + G6WriteIteratorIsAllocated = false; + } + if (pG6WriteIterator->currGraphBuff == NULL) + { + ErrorMessage("G6WriteIterator's currGraphBuff is NULL.\n"); + G6WriteIteratorIsAllocated = false; + } + if (pG6WriteIterator->columnOffsets == NULL) + { + ErrorMessage("G6WriteIterator's columnOffsets is NULL.\n"); + G6WriteIteratorIsAllocated = false; + } + if (pG6WriteIterator->currGraph == NULL) + { + ErrorMessage("G6WriteIterator's currGraph is NULL.\n"); + G6WriteIteratorIsAllocated = false; + } + if (gp_getN(pG6WriteIterator->currGraph) == 0) + { + ErrorMessage("G6WriteIterator's currGraph does not contain a valid " + "graph.\n"); + G6WriteIteratorIsAllocated = false; + } + } + + return G6WriteIteratorIsAllocated; +} + +int getNumGraphsWritten(G6WriteIteratorP pG6WriteIterator, int *pNumGraphsRead) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*pNumGraphsRead) = pG6WriteIterator->numGraphsWritten; + + return OK; +} + +int getOrderOfGraphToWrite(G6WriteIteratorP pG6WriteIterator, int *pGraphOrder) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*pGraphOrder) = pG6WriteIterator->graphOrder; + + return OK; +} + +int getPointerToGraphToWrite(G6WriteIteratorP pG6WriteIterator, graphP *ppGraph) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("[ERROR] G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*ppGraph) = pG6WriteIterator->currGraph; + + return OK; +} + +int beginG6WriteIterationToG6String(G6WriteIteratorP pG6WriteIterator) +{ + return beginG6WriteIterationToG6StrOrFile( + pG6WriteIterator, + sf_New(NULL, NULL, WRITETEXT)); +} + +int beginG6WriteIterationToG6FilePath(G6WriteIteratorP pG6WriteIterator, char *outputFilename) +{ + return beginG6WriteIterationToG6StrOrFile( + pG6WriteIterator, + sf_New(NULL, outputFilename, WRITETEXT)); +} + +int beginG6WriteIterationToG6StrOrFile(G6WriteIteratorP pG6WriteIterator, strOrFileP outputContainer) +{ + int exitCode = OK; + + if (pG6WriteIterator == NULL) + { + ErrorMessage("Invalid parameter pG6WriteIterator.\n"); + return NOTOK; + } + + if (sf_ValidateStrOrFile(outputContainer) != OK) + { + ErrorMessage("Invalid strOrFile output container provided.\n"); + return NOTOK; + } + + pG6WriteIterator->g6Output = outputContainer; + + exitCode = _beginG6WriteIteration(pG6WriteIterator); + + if (exitCode != OK) + ErrorMessage("Unable to begin .g6 write iteration to given strOrFile output container.\n"); + + return exitCode; +} + +int _beginG6WriteIteration(G6WriteIteratorP pG6WriteIterator) +{ + int exitCode = OK; + + char const *g6Header = ">>graph6<<"; + if (sf_fputs(g6Header, pG6WriteIterator->g6Output) < 0) + { + ErrorMessage("Unable to fputs header to g6Output.\n"); + return NOTOK; + } + + pG6WriteIterator->graphOrder = gp_getN(pG6WriteIterator->currGraph); + + pG6WriteIterator->columnOffsets = (int *)calloc(pG6WriteIterator->graphOrder + 1, sizeof(int)); + + if (pG6WriteIterator->columnOffsets == NULL) + { + ErrorMessage("Unable to allocate memory for column offsets.\n"); + return NOTOK; + } + + _precomputeColumnOffsets(pG6WriteIterator->columnOffsets, pG6WriteIterator->graphOrder); + + pG6WriteIterator->numCharsForGraphOrder = _getNumCharsForGraphOrder(pG6WriteIterator->graphOrder); + pG6WriteIterator->numCharsForGraphEncoding = _getNumCharsForGraphEncoding(pG6WriteIterator->graphOrder); + // Must add 3 bytes for newline, possible carriage return, and null terminator + pG6WriteIterator->currGraphBuffSize = pG6WriteIterator->numCharsForGraphOrder + pG6WriteIterator->numCharsForGraphEncoding + 3; + pG6WriteIterator->currGraphBuff = (char *)calloc(pG6WriteIterator->currGraphBuffSize, sizeof(char)); + + if (pG6WriteIterator->currGraphBuff == NULL) + { + ErrorMessage("Unable to allocate memory for currGraphBuff.\n"); + exitCode = NOTOK; + } + + return exitCode; +} + +void _precomputeColumnOffsets(int *columnOffsets, int graphOrder) +{ + if (columnOffsets == NULL) + { + ErrorMessage("Must allocate columnOffsets memory before precomputation.\n"); + return; + } + + columnOffsets[0] = 0; + columnOffsets[1] = 0; + for (int i = 2; i <= graphOrder; i++) + columnOffsets[i] = columnOffsets[i - 1] + (i - 1); +} + +int writeGraphUsingG6WriteIterator(G6WriteIteratorP pG6WriteIterator) +{ + int exitCode = OK; + + exitCode = _encodeAdjMatAsG6(pG6WriteIterator); + if (exitCode != OK) + { + ErrorMessage("Error converting adjacency matrix to g6 format.\n"); + return exitCode; + } + + exitCode = _printEncodedGraph(pG6WriteIterator); + if (exitCode != OK) + ErrorMessage("Unable to output g6 encoded graph to string-or-file container.\n"); + + return exitCode; +} + +int _encodeAdjMatAsG6(G6WriteIteratorP pG6WriteIterator) +{ + int exitCode = OK; + + char *g6Encoding = NULL; + int *columnOffsets = NULL; + graphP pGraph = NULL; + + int graphOrder = 0; + int numCharsForGraphOrder = 0; + int numCharsForGraphEncoding = 0; + int totalNumCharsForOrderAndGraph = 0; + + int u = NIL, v = NIL, e = NIL; + int charOffset = 0; + int bitPositionPower = 0; + + if (!_isG6WriteIteratorAllocated(pG6WriteIterator)) + { + ErrorMessage("Unable to encode graph with invalid G6WriteIterator\n"); + return NOTOK; + } + + g6Encoding = pG6WriteIterator->currGraphBuff; + columnOffsets = pG6WriteIterator->columnOffsets; + pGraph = pG6WriteIterator->currGraph; + + // memset ensures all bits are zero, which means we only need to set the bits + // that correspond to an edge; this also takes care of padding zeroes for us + memset(pG6WriteIterator->currGraphBuff, 0, (pG6WriteIterator->currGraphBuffSize) * sizeof(char)); + + graphOrder = pG6WriteIterator->graphOrder; + numCharsForGraphOrder = pG6WriteIterator->numCharsForGraphOrder; + numCharsForGraphEncoding = pG6WriteIterator->numCharsForGraphEncoding; + totalNumCharsForOrderAndGraph = numCharsForGraphOrder + numCharsForGraphEncoding; + + if (graphOrder > 62) + { + int i, intermediate; + g6Encoding[0] = 126; + // bytes 1 through 3 will be populated with the 18-bit representation of the graph order + intermediate = -1; + for (i = 0; i < 3; i++) + { + intermediate = graphOrder >> (6 * i); + g6Encoding[3 - i] = intermediate & 63; + g6Encoding[3 - i] += 63; + } + } + else if (graphOrder > 1 && graphOrder < 63) + { + g6Encoding[0] = (char)(graphOrder + 63); + } + + u = v = e = NIL; + exitCode = _getFirstEdge(pGraph, &e, &u, &v); + + if (exitCode != OK) + { + ErrorMessage("Unable to fetch first edge in graph.\n"); + return exitCode; + } + + charOffset = bitPositionPower = 0; + while (u != NIL && v != NIL) + { + // The internal graph representation is usually 1-based, but may be 0-based, so + // one must subtract the index of the first vertex (i.e. result of gp_GetFirstVertex) + // because the .g6 format is 0-based + u -= gp_GetFirstVertex(theGraph); + v -= gp_GetFirstVertex(theGraph); + + // The columnOffset machinery assumes that we are traversing the edges represented in + // the upper-triangular matrix. Since we are dealing with simple graphs, if (v, u) + // exists, then (u, v) exists, and so the edge is indicated by a 1 in row = min(u, v) + // and col = max(u, v) in the upper-triangular adjacency matrix. + if (v < u) + { + int tempVert = v; + v = u; + u = tempVert; + } + + // (columnOffsets[v] + u) describes the bit index of the current edge + // given the column and row in the adjacency matrix representation; + // the byte is floor((columnOffsets[v] + u) / 6) and the we determine which + // bit to set in that byte by left-shifting 1 by (5 - ((columnOffsets[v] + u) % 6)) + // (transforming the ((columnOffsets[v] + u) % 6)th bit from the left to the + // (5 - ((columnOffsets[v] + u) % 6))th bit from the right) + charOffset = numCharsForGraphOrder + ((columnOffsets[v] + u) / 6); + bitPositionPower = 5 - ((columnOffsets[v] + u) % 6); + + g6Encoding[charOffset] |= (1u << bitPositionPower); + + exitCode = _getNextEdge(pGraph, &e, &u, &v); + + if (exitCode != OK) + { + ErrorMessage("Unable to fetch next edge in graph.\n"); + free(columnOffsets); + free(g6Encoding); + return exitCode; + } + } + + // Bytes corresponding to graph order have already been modified to + // correspond to printable ascii character (i.e. by adding 63); must + // now do the same for bytes corresponding to edge lists + for (int i = numCharsForGraphOrder; i < totalNumCharsForOrderAndGraph; i++) + g6Encoding[i] += 63; + + return exitCode; +} + +int _getFirstEdge(graphP theGraph, int *e, int *u, int *v) +{ + if (theGraph == NULL) + return NOTOK; + + if ((*e) >= gp_EdgeInUseIndexBound(theGraph)) + { + ErrorMessage("First edge is outside bounds."); + return NOTOK; + } + + (*e) = gp_GetFirstEdge(theGraph); + + return _getNextInUseEdge(theGraph, e, u, v); +} + +int _getNextEdge(graphP theGraph, int *e, int *u, int *v) +{ + if (theGraph == NULL) + return NOTOK; + + (*e) += 2; + + return _getNextInUseEdge(theGraph, e, u, v); +} + +int _getNextInUseEdge(graphP theGraph, int *e, int *u, int *v) +{ + int exitCode = OK; + int EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + + (*u) = NIL; + (*v) = NIL; + + if ((*e) < EsizeOccupied) + { + while (!gp_EdgeInUse(theGraph, (*e))) + { + (*e) += 2; + if ((*e) >= EsizeOccupied) + break; + } + + if ((*e) < EsizeOccupied && gp_EdgeInUse(theGraph, (*e))) + { + (*u) = gp_GetNeighbor(theGraph, (*e)); + (*v) = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, (*e))); + } + } + + return exitCode; +} + +int _printEncodedGraph(G6WriteIteratorP pG6WriteIterator) +{ + int exitCode = OK; + + if (pG6WriteIterator->g6Output == NULL) + { + ErrorMessage("Unable to print to NULL string-or-file container.\n"); + return NOTOK; + } + + if (pG6WriteIterator->currGraphBuff == NULL || strlen(pG6WriteIterator->currGraphBuff) == 0) + { + ErrorMessage("Unable to print; g6 encoding is empty.\n"); + return NOTOK; + } + + if (sf_fputs(pG6WriteIterator->currGraphBuff, pG6WriteIterator->g6Output) < 0) + { + ErrorMessage("Failed to output all characters of g6 encoding.\n"); + exitCode = NOTOK; + } + + if (sf_fputs("\n", pG6WriteIterator->g6Output) < 0) + { + ErrorMessage("Failed to put line terminator after g6 encoding.\n"); + exitCode = NOTOK; + } + + return exitCode; +} + +int endG6WriteIteration(G6WriteIteratorP pG6WriteIterator) +{ + int exitCode = OK; + + if (pG6WriteIterator != NULL) + { + if (pG6WriteIterator->g6Output != NULL) + sf_Free(&(pG6WriteIterator->g6Output)); + + if (pG6WriteIterator->currGraphBuff != NULL) + { + free(pG6WriteIterator->currGraphBuff); + pG6WriteIterator->currGraphBuff = NULL; + } + + if (pG6WriteIterator->columnOffsets != NULL) + { + free((pG6WriteIterator->columnOffsets)); + pG6WriteIterator->columnOffsets = NULL; + } + } + + return exitCode; +} + +int freeG6WriteIterator(G6WriteIteratorP *ppG6WriteIterator) +{ + int exitCode = OK; + + if (ppG6WriteIterator != NULL && (*ppG6WriteIterator) != NULL) + { + if ((*ppG6WriteIterator)->g6Output != NULL) + sf_Free(&((*ppG6WriteIterator)->g6Output)); + + (*ppG6WriteIterator)->numGraphsWritten = 0; + (*ppG6WriteIterator)->graphOrder = 0; + + if ((*ppG6WriteIterator)->currGraphBuff != NULL) + { + free((*ppG6WriteIterator)->currGraphBuff); + (*ppG6WriteIterator)->currGraphBuff = NULL; + } + + if ((*ppG6WriteIterator)->columnOffsets != NULL) + { + free(((*ppG6WriteIterator)->columnOffsets)); + (*ppG6WriteIterator)->columnOffsets = NULL; + } + + // N.B. The G6WriteIterator doesn't "own" the graph, so we don't free it. + (*ppG6WriteIterator)->currGraph = NULL; + + free((*ppG6WriteIterator)); + (*ppG6WriteIterator) = NULL; + } + + return exitCode; +} + +int _WriteGraphToG6FilePath(graphP pGraph, char *g6OutputFilename) +{ + strOrFileP outputContainer = sf_New(NULL, g6OutputFilename, WRITETEXT); + if (outputContainer == NULL) + { + ErrorMessage("Unable to allocate outputContainer to which to write.\n"); + return NOTOK; + } + + return _WriteGraphToG6StrOrFile(pGraph, outputContainer, NULL); +} + +int _WriteGraphToG6String(graphP pGraph, char **g6OutputStr) +{ + strOrFileP outputContainer = sf_New(NULL, NULL, WRITETEXT); + if (outputContainer == NULL) + { + ErrorMessage("Unable to allocate outputContainer to which to write.\n"); + return NOTOK; + } + + // N.B. If g6OutputStr is a pointer to a pointer to a block of memory that + // has been allocated, i.e. if g6OutputStr != NULL && (*g6OutputStr) != NULL + // then an error will be emitted by _WriteGraphToG6StrOrFile(). + // N.B. Once the graph is successfully written, the string is taken from + // the G6WriteIterator's outputContainer and assigned to (*g6OutputStr) + // before ending G6 write iteration + return _WriteGraphToG6StrOrFile(pGraph, outputContainer, g6OutputStr); +} + +int _WriteGraphToG6StrOrFile(graphP pGraph, strOrFileP outputContainer, char **outputStr) +{ + int exitCode = OK; + + G6WriteIteratorP pG6WriteIterator = NULL; + + if (sf_ValidateStrOrFile(outputContainer) != OK) + { + ErrorMessage("Invalid G6 output container.\n"); + return NOTOK; + } + + if (outputContainer->theStr != NULL && (outputStr == NULL)) + { + ErrorMessage("If writing G6 to string, must provide pointer-pointer " + "to allow _WriteGraphToG6StrOrFile() to assign the address " + "of the output string.\n"); + return NOTOK; + } + + if (outputStr != NULL && (*outputStr) != NULL) + { + ErrorMessage("(*outputStr) should not point to allocated memory."); + return NOTOK; + } + + exitCode = allocateG6WriteIterator(&pG6WriteIterator, pGraph); + if (exitCode != OK) + { + ErrorMessage("Unable to allocate G6WriteIterator.\n"); + freeG6WriteIterator(&pG6WriteIterator); + return exitCode; + } + + exitCode = beginG6WriteIterationToG6StrOrFile(pG6WriteIterator, outputContainer); + if (exitCode != OK) + { + ErrorMessage("Unable to begin G6 write iteration.\n"); + freeG6WriteIterator(&pG6WriteIterator); + return exitCode; + } + + exitCode = writeGraphUsingG6WriteIterator(pG6WriteIterator); + if (exitCode != OK) + ErrorMessage("Unable to write graph using G6WriteIterator.\n"); + else + { + if (outputStr != NULL && pG6WriteIterator->g6Output->theStr != NULL) + (*outputStr) = sf_takeTheStr(pG6WriteIterator->g6Output); + } + + if (endG6WriteIteration(pG6WriteIterator) != OK) + { + ErrorMessage("Unable to end G6 write iteration.\n"); + exitCode = NOTOK; + } + + if (freeG6WriteIterator(&pG6WriteIterator) != OK) + { + ErrorMessage("Unable to free G6Writer.\n"); + exitCode = NOTOK; + } + + return exitCode; +} diff --git a/planarity_src/c/graphLib/io/g6-write-iterator.h b/planarity_src/c/graphLib/io/g6-write-iterator.h new file mode 100644 index 0000000..c855d8f --- /dev/null +++ b/planarity_src/c/graphLib/io/g6-write-iterator.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef G6_WRITE_ITERATOR +#define G6_WRITE_ITERATOR + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "../graph.h" +#include "strOrFile.h" + + typedef struct + { + strOrFileP g6Output; + int numGraphsWritten; + + int graphOrder; + int numCharsForGraphOrder; + int numCharsForGraphEncoding; + int currGraphBuffSize; + char *currGraphBuff; + + int *columnOffsets; + + graphP currGraph; + } G6WriteIterator; + + typedef G6WriteIterator *G6WriteIteratorP; + + int allocateG6WriteIterator(G6WriteIteratorP *, graphP); + bool _isG6WriteIteratorAllocated(G6WriteIteratorP); + + int getNumGraphsWritten(G6WriteIteratorP, int *); + int getOrderOfGraphToWrite(G6WriteIteratorP, int *); + int getPointerToGraphToWrite(G6WriteIteratorP, graphP *); + + int beginG6WriteIterationToG6String(G6WriteIteratorP); + int beginG6WriteIterationToG6FilePath(G6WriteIteratorP, char *); + int beginG6WriteIterationToG6StrOrFile(G6WriteIteratorP, strOrFileP); + int _beginG6WriteIteration(G6WriteIteratorP); + void _precomputeColumnOffsets(int *, int); + + int writeGraphUsingG6WriteIterator(G6WriteIteratorP); + + int _encodeAdjMatAsG6(G6WriteIteratorP); + int _getFirstEdge(graphP, int *, int *, int *); + int _getNextEdge(graphP, int *, int *, int *); + int _getNextInUseEdge(graphP, int *, int *, int *); + + int _printEncodedGraph(G6WriteIteratorP); + + int endG6WriteIteration(G6WriteIteratorP); + + int freeG6WriteIterator(G6WriteIteratorP *); + + int _WriteGraphToG6FilePath(graphP, char *); + int _WriteGraphToG6String(graphP, char **); + int _WriteGraphToG6StrOrFile(graphP, strOrFileP, char **); + +#ifdef __cplusplus +} +#endif + +#endif /* G6_WRITE_ITERATOR */ diff --git a/planarity_src/c/graphLib/io/graphIO.c b/planarity_src/c/graphLib/io/graphIO.c new file mode 100644 index 0000000..41d24f2 --- /dev/null +++ b/planarity_src/c/graphLib/io/graphIO.c @@ -0,0 +1,1107 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include +#include + +#include "../graph.h" + +/* Private functions (exported to system) */ + +int _ReadGraph(graphP theGraph, strOrFileP inputContainer); +int _ReadAdjMatrix(graphP theGraph, strOrFileP inputContainer); +int _ReadAdjList(graphP theGraph, strOrFileP inputContainer); +int _ReadLEDAGraph(graphP theGraph, strOrFileP inputContainer); +int _ReadPostprocess(graphP theGraph, char *extraData); + +int _WriteGraph(graphP theGraph, strOrFileP *outputContainer, char **pOutputStr, int Mode); +int _WriteAdjList(graphP theGraph, strOrFileP outputContainer); +int _WriteAdjMatrix(graphP theGraph, strOrFileP outputContainer); +int _WriteDebugInfo(graphP theGraph, strOrFileP outputContainer); +int _WritePostprocess(graphP theGraph, char **pExtraData); + +// These prototypes are defined if LOGGING is defined, but +// if LOGGING is not defined, then these declarations help +// to get rid of missingprototype warnings. +#ifndef LOGGING +void _LogLine(const char *Line); +void _Log(const char *Line); + +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); +#endif + +/* Private functions */ +char _GetEdgeTypeChar(graphP theGraph, int e); +char _GetVertexObstructionTypeChar(graphP theGraph, int v); + +/******************************************************************** + _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, strOrFileP inputContainer) + +{ + int N = -1; + int v, w, Flag; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + // Read the number of vertices from the first line of the file + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (sf_ReadInteger(&N, inputContainer) != OK) + return NOTOK; + + // 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 (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (sf_ReadSingleDigit(&Flag, inputContainer) != OK) + return NOTOK; + // N.B. Currently do not allow edge-weights in Adjacency + // Matrix format + if (Flag != 0 && Flag != 1) + return NOTOK; + + // Add the edge (v, w) if the flag is raised + if (Flag) + { + if (gp_DynamicAddEdge(theGraph, v, 0, w, 0) != OK) + return NOTOK; + } + } + } + + return OK; +} + +/******************************************************************** + _ReadAdjList() + This function reads the graph in adjacency list format. + + The graph format is: + On the first line : N= + 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, strOrFileP inputContainer) +{ + int N = -1; + int v, W, adjList, e, indexValue, ErrorCode; + int zeroBased = FALSE; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + // Skip the "N=" and then read the N value for number of vertices + if (sf_ReadSkipChar(inputContainer) != OK) + return NOTOK; + + if (sf_ReadSkipChar(inputContainer) != OK) + return NOTOK; + + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (sf_ReadInteger(&N, inputContainer) != OK) + return NOTOK; + + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + // 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 (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&indexValue, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + 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 (sf_ReadSkipChar(inputContainer) != OK) + return NOTOK; + + // 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 (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&W, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + 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_DynamicAddEdge(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_DynamicAddEdge(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); + // This macro expands to constant conditional expression, but it's the proper use of the API + } + } + } + + // 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); + // This macro expands to constant conditional expression, but it's the proper use of the API + } + } + + 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, strOrFileP inputContainer) +{ + char Line[MAXLINE + 1]; + int N = -1; + int graphType, M, m, u, v, ErrorCode; + int zeroBasedOffset = gp_GetFirstVertex(theGraph) == 0 ? 1 : 0; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + /* + N.B. Skip the lines that say LEDA.GRAPH and give the node and + edge types then determine if graph is directed (-1) or + undirected (-2); we only support undirected graphs at this time. + */ + for (int i = 0; i < 3; i++) + if (sf_ReadSkipLineRemainder(inputContainer) != OK) + return NOTOK; + + if (sf_ReadInteger(&graphType, inputContainer) != OK) + return NOTOK; + + // N.B. We currently only support undirected graphs + if (graphType != -2) + return NOTOK; + + /* + Skip any preceding whitespace, read the number of vertices N, + and skip any subsequent whitespace. + */ + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&N, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (gp_InitGraph(theGraph, N) != OK) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (sf_fgets(Line, MAXLINE, inputContainer) == NULL) + return NOTOK; + + /* Read the number of edges */ + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&M, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + /* Read and add each edge, omitting loops and parallel edges */ + for (m = 0; m < M; m++) + { + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&u, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&v, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipLineRemainder(inputContainer) != OK) + return NOTOK; + + if (u != v && !gp_IsNeighbor(theGraph, u - zeroBasedOffset, v - zeroBasedOffset)) + { + if ((ErrorCode = gp_DynamicAddEdge(theGraph, u - zeroBasedOffset, 0, v - zeroBasedOffset, 0)) != OK) + return ErrorCode; + } + } + + if (zeroBasedOffset) + theGraph->internalFlags |= FLAGS_ZEROBASEDIO; + + return OK; +} + +/******************************************************************** + gp_Read() + + Populates theGraph from the contents of the input file with path + FileName. + + 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 const *FileName) +{ + strOrFileP inputContainer = sf_New(NULL, FileName, READTEXT); + if (inputContainer == NULL) + return NOTOK; + + return _ReadGraph(theGraph, inputContainer); +} + +/******************************************************************** + gp_ReadFromString() + + Populates theGraph using the information stored in inputStr. + + The ownership of inputStr is transferred from the caller; it is + assigned to a strOrFile container and ownership is transferred to + the internal helper function _ReadGraph(), which then handles + freeing this memory. + + Returns NOTOK for any error, or OK otherwise + ********************************************************************/ + +int gp_ReadFromString(graphP theGraph, char *inputStr) +{ + strOrFileP inputContainer = sf_New(inputStr, NULL, READTEXT); + if (inputContainer == NULL) + { + if (inputStr != NULL) + free(inputStr); + inputStr = NULL; + return NOTOK; + } + + return _ReadGraph(theGraph, inputContainer); +} + +/******************************************************************** + _ReadGraph() + + Determines the graph input format by parsing the first line of the + input stream or string contained by the inputContainer and calling the + appropriate read function, then then cleans up the inputContainer 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. + ********************************************************************/ + +int _ReadGraph(graphP theGraph, strOrFileP inputContainer) +{ + int RetVal = OK; + bool extraDataAllowed = false; + char lineBuff[MAXLINE + 1]; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + if (sf_fgets(lineBuff, MAXLINE, inputContainer) == NULL) + { + sf_Free(&inputContainer); + return NOTOK; + } + + if (sf_ungets(lineBuff, inputContainer) != OK) + { + sf_Free(&inputContainer); + return NOTOK; + } + + if (strncmp(lineBuff, "LEDA.GRAPH", strlen("LEDA.GRAPH")) == 0) + { + RetVal = _ReadLEDAGraph(theGraph, inputContainer); + } + else if (strncmp(lineBuff, "N=", strlen("N=")) == 0) + { + RetVal = _ReadAdjList(theGraph, inputContainer); + if (RetVal == OK) + extraDataAllowed = true; + } + else if (isdigit(lineBuff[0])) + { + RetVal = _ReadAdjMatrix(theGraph, inputContainer); + if (RetVal == OK) + extraDataAllowed = true; + } + else + { + RetVal = _ReadGraphFromG6StrOrFile(theGraph, inputContainer); + // N.B. Unlike the other _Read functions, we are relinquishing + // ownership of inputContainer to the G6ReadIterator, which + // calls sf_Free() when ending iteration. This assignment + // prevents calling free on alread-freed memory. + inputContainer = NULL; + } + + // The possibility of "extra data" is not allowed for .g6 format: + // .g6 files may contain multiple graphs, which are not valid input + // for the extra data readers (i.e. fpReadPostProcess) Additionally, + // we don't want to add extra data if the graph is nonembeddable, as + // the FILE pointer isn't necessarily advanced past the graph + // encoding unless OK is returned. + if (extraDataAllowed) + { + char charAfterGraphRead = EOF; + if ((charAfterGraphRead = sf_getc(inputContainer)) != EOF) + { + if (sf_ungetc(charAfterGraphRead, inputContainer) != charAfterGraphRead) + RetVal = NOTOK; + else + { + strBufP extraData = sb_New(0); + if (extraData == NULL) + RetVal = NOTOK; + else + { + // FIXME: how do I distinguish between "there's no more content on input stream" and "I've hit an error state" + while (sf_fgets(lineBuff, MAXLINE, inputContainer) != NULL) + { + if (sb_ConcatString(extraData, lineBuff) != OK) + { + RetVal = NOTOK; + break; + } + } + + if (sb_GetSize(extraData) > 0) + RetVal = theGraph->functions.fpReadPostprocess(theGraph, sb_GetReadString(extraData)); + + sb_Free(&extraData); + extraData = NULL; + } + } + } + } + + if (inputContainer != NULL) + sf_Free(&inputContainer); + inputContainer = NULL; + + return RetVal; +} + +int _ReadPostprocess(graphP theGraph, char *extraData) +{ + 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, strOrFileP outputContainer) +{ + int v, e; + int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; + char numberStr[MAXCHARSFOR32BITINT + 1]; + memset(numberStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char)); + + if (theGraph == NULL || sf_ValidateStrOrFile(outputContainer) != OK) + return NOTOK; + + // Write the number of vertices of the graph to the file or string buffer + if (sprintf(numberStr, "N=%d\n", theGraph->N) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + + // Write the adjacency list of each vertex + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (sprintf(numberStr, "%d:", v - zeroBasedOffset) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + + e = gp_GetLastArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) + { + if (sprintf(numberStr, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + 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 (sprintf(numberStr, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + 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, strOrFileP outputContainer) +{ + int v, e, K; + char *Row = NULL; + char numberStr[MAXCHARSFOR32BITINT + 1]; + memset(numberStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char)); + + if (theGraph == NULL || sf_ValidateStrOrFile(outputContainer) != OK) + return NOTOK; + + // Write the number of vertices in the graph to the file or string buffer + if (sprintf(numberStr, "%d\n", theGraph->N) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + 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 (sf_fputs(Row, outputContainer) == EOF) + return NOTOK; + } + + 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, strOrFileP outputContainer) +{ + int v, e, EsizeOccupied; + char lineBuf[MAXLINE + 1]; + memset(lineBuf, '\0', (MAXLINE + 1) * sizeof(char)); + + if (theGraph == NULL || sf_ValidateStrOrFile(outputContainer) != OK) + return NOTOK; + + /* Print parent copy vertices and their adjacency lists */ + if (sprintf(lineBuf, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (sprintf(lineBuf, "%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)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (sprintf(lineBuf, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + e = gp_GetNextArc(theGraph, e); + } + + if (sprintf(lineBuf, " %d\n", NIL) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + + /* 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; + + if (sprintf(lineBuf, "%d(copy of=%d, DFS child=%d):", + v, gp_GetVertexIndex(theGraph, v), + gp_GetDFSChildFromRoot(theGraph, v)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (sprintf(lineBuf, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + e = gp_GetNextArc(theGraph, e); + } + + if (sprintf(lineBuf, " %d\n", NIL) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + + /* Print information about vertices and root copy (virtual) vertices */ + if (sf_fputs("\nVERTEX INFORMATION\n", outputContainer) == EOF) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (sprintf(lineBuf, "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)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) + { + if (gp_VirtualVertexNotInUse(theGraph, v)) + continue; + + if (sprintf(lineBuf, "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)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + + /* Print information about edges */ + + if (sf_fputs("\nEDGE INFORMATION\n", outputContainer) == EOF) + return NOTOK; + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) + { + if (gp_EdgeInUse(theGraph, e)) + { + if (sprintf(lineBuf, "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)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + gp_Write() + Writes theGraph into the file. + Pass "stdout" or "stderr" to FileName to write to the corresponding stream + Pass WRITE_G6, 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 const *FileName, int Mode) +{ + int RetVal; + strOrFileP outputContainer = NULL; + + if (theGraph == NULL || FileName == NULL) + return NOTOK; + + if (strcmp(FileName, "nullwrite") == 0) + return OK; + + outputContainer = sf_New(NULL, FileName, WRITETEXT); + if (outputContainer == NULL) + return NOTOK; + + RetVal = _WriteGraph(theGraph, &outputContainer, NULL, Mode); + + if (outputContainer != NULL) + sf_Free(&outputContainer); + outputContainer = NULL; + + 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_G6, 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; + strOrFileP outputContainer = NULL; + + if (theGraph == NULL || pOutputStr == NULL) + return NOTOK; + + outputContainer = sf_New(NULL, NULL, WRITETEXT); + if (outputContainer == NULL) + return NOTOK; + + RetVal = _WriteGraph(theGraph, &outputContainer, pOutputStr, Mode); + + // N.B. Since we pass ownership of the outputContainer to the + // G6WriteIterator when we WRITE_G6, we make sure to take the string + // *before* we endG6WriteIteration(), since that calls sf_Free() on the + // g6Output (i.e. outputContainer) and therefore sb_Free() on theStr. This + // means that we need to make sure outputContainer and theStr it contains + // are both non-NULL before trying to take the string, as WRITE_ADJLIST, + // WRITE_ADJMATRIX, and WRITE_DEBUGINFO do *not* clean up the + // outputContainer. + if (RetVal == OK && outputContainer != NULL) + { + (*pOutputStr) = sf_takeTheStr(outputContainer); + } + + if ((*pOutputStr) == NULL || strlen(*pOutputStr) == 0) + RetVal = NOTOK; + + if (outputContainer != NULL) + sf_Free(&outputContainer); + outputContainer = NULL; + + return RetVal; +} + +/******************************************************************** + _WriteGraph() + Writes theGraph into the strOrFile container. + + Pass WRITE_G6, 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 _WriteGraph(graphP theGraph, strOrFileP *outputContainer, char **pOutputStr, int Mode) +{ + int RetVal = OK; + + switch (Mode) + { + case WRITE_G6: + RetVal = _WriteGraphToG6StrOrFile(theGraph, (*outputContainer), pOutputStr); + // Since G6WriteIterator owns the outputContainer, it'll + // free it, so don't want to try to double-free + (*outputContainer) = NULL; + break; + case WRITE_ADJLIST: + RetVal = _WriteAdjList(theGraph, (*outputContainer)); + break; + case WRITE_ADJMATRIX: + RetVal = _WriteAdjMatrix(theGraph, (*outputContainer)); + break; + case WRITE_DEBUGINFO: + RetVal = _WriteDebugInfo(theGraph, (*outputContainer)); + break; + default: + RetVal = NOTOK; + break; + } + + if (RetVal == OK) + { + char *extraData = NULL; + + RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData); + + if (extraData != NULL) + { + if (sf_fputs(extraData, (*outputContainer)) == EOF) + RetVal = NOTOK; + free(extraData); + } + } + + return RetVal; +} + +/******************************************************************** + _WritePostprocess() + + By default, no additional information is written. + ********************************************************************/ + +int _WritePostprocess(graphP theGraph, char **pExtraData) +{ + 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 const *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 const *Str) +{ + _Log(Str); + _Log("\n"); +} + +static char LogStr[MAXLINE + 1]; + +char *_MakeLogStr1(char *format, int one) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(LogStr, format, one); +#pragma GCC diagnostic pop + return LogStr; +} + +char *_MakeLogStr2(char *format, int one, int two) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(LogStr, format, one, two); +#pragma GCC diagnostic pop + return LogStr; +} + +char *_MakeLogStr3(char *format, int one, int two, int three) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(LogStr, format, one, two, three); +#pragma GCC diagnostic pop + return LogStr; +} + +char *_MakeLogStr4(char *format, int one, int two, int three, int four) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(LogStr, format, one, two, three, four); +#pragma GCC diagnostic pop + return LogStr; +} + +char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(LogStr, format, one, two, three, four, five); +#pragma GCC diagnostic pop + return LogStr; +} diff --git a/planarity_src/c/graphLib/io/strOrFile.c b/planarity_src/c/graphLib/io/strOrFile.c new file mode 100644 index 0000000..8355769 --- /dev/null +++ b/planarity_src/c/graphLib/io/strOrFile.c @@ -0,0 +1,731 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include +#include +#include +#include + +#include "../lowLevelUtils/appconst.h" +#include "strOrFile.h" + +/******************************************************************** + sf_New() + + Accepts a FILE pointer XOR a string, which are owned by the container. + + Returns the allocated string-or-file container, or NULL on error. + ********************************************************************/ + +strOrFileP sf_New(char const *theStr, char const *fileName, char const *ioMode) +{ + strOrFileP theStrOrFile; + int containerType = 0; + + if ((fileName != NULL) && (theStr != NULL)) + return NULL; + + theStrOrFile = (strOrFileP)calloc(1, sizeof(strOrFile)); + if (theStrOrFile != NULL) + { + if (fileName != NULL) + { + FILE *pFile = NULL; + + // N.B. If the ioMode is specified but is neither + // READTEXT nor WRITETEXT, error out + if (ioMode != NULL && + strncmp(ioMode, READTEXT, strlen(READTEXT)) != 0 && + strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + // N.B. If the fileName indicates a stream, then make sure + // ioMode is correct. Since we previously made sure that + // non-NULL ioMode must either refer to READTEXT or WRITETEXT, + // we don't have to worry about freeing up the former string + // before re-assigning. + if (strcmp(fileName, "stdin") == 0) + { + if (ioMode != NULL && strncmp(ioMode, READTEXT, strlen(READTEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + pFile = stdin; + containerType = INPUT_CONTAINER; + } + else if (strcmp(fileName, "stdout") == 0) + { + if (ioMode != NULL && strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + pFile = stdout; + containerType = OUTPUT_CONTAINER; + } + else if (strcmp(fileName, "stderr") == 0) + { + if (ioMode != NULL && strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + pFile = stderr; + containerType = OUTPUT_CONTAINER; + } + else + { + // N.B. Clean up and return NULL if: + // - fileName is not one of stdin/stdout/stderr, and + // if the ioMode is not given + // - ioMode is given but is neither READTEXT nor + // WRITETEXT + if (ioMode == NULL || + (pFile = fopen(fileName, ioMode)) == NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + + if (strncmp(ioMode, READTEXT, strlen(READTEXT)) == 0) + containerType = INPUT_CONTAINER; + else if (strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) == 0) + containerType = OUTPUT_CONTAINER; + } + + theStrOrFile->pFile = pFile; + } + else + { + strBufP strBufToAssign = NULL; + + if (strncmp(ioMode, READTEXT, strlen(READTEXT)) == 0) + containerType = INPUT_CONTAINER; + else if (strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) == 0) + containerType = OUTPUT_CONTAINER; + + // N.B. If you're writing to string (since fileName == NULL), but + // theStr has already been assigned some pointer, the container is + // invalid. One must sf_New(NULL, NULL, WRITETEXT) before handing + // off ownership of the container; when processing is done, before + // you free the owner of the output container, you must + // sf_takeTheStr(theStrOrFile) and return that pointer to the caller + if (containerType != INPUT_CONTAINER && theStr != NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + + if ((strBufToAssign = sb_New(0)) == NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + + if (theStr != NULL && sb_ConcatString(strBufToAssign, theStr) != OK) + { + sb_Free(&strBufToAssign); + sf_Free(&theStrOrFile); + return NULL; + } + + theStrOrFile->theStr = strBufToAssign; + } + + theStrOrFile->containerType = containerType; + + theStrOrFile->ungetBuf = sp_New(MAXLINE); + if (theStrOrFile->ungetBuf == NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + } + + return theStrOrFile; +} + +/******************************************************************** + sf_ValidateStrOrFile() + + Ensures that theStrOrFile: + 1. Is not NULL + 2. Has ungetBuf allocated + 3. Both pFile and theStr are not NULL + 4. Both pFile and theStr are not both assigned (since this container + should only contain one source). + 5. containerType is either set to INPUT_CONTAINER or OUTPUT_CONTAINER + + Returns NOTOK if any of these conditions are not met, otherwise OK. + ********************************************************************/ + +int sf_ValidateStrOrFile(strOrFileP theStrOrFile) +{ + if (theStrOrFile == NULL || + theStrOrFile->ungetBuf == NULL || + (theStrOrFile->pFile == NULL && theStrOrFile->theStr == NULL) || + (theStrOrFile->pFile != NULL && theStrOrFile->theStr != NULL) || + (theStrOrFile->containerType != INPUT_CONTAINER && + theStrOrFile->containerType != OUTPUT_CONTAINER)) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_getc() + + If strOrFileP has a non-empty ungetBuf, pop and return the character. + If the ungetBuf is empty, then we'll read from pFile using getc() OR + from theStr by fetching the character at theStrPos and incrementing + theStrPos. + ********************************************************************/ + +char sf_getc(strOrFileP theStrOrFile) +{ + char theChar = EOF; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return EOF; + + if ((theStrOrFile->ungetBuf != NULL) && (sp_GetCurrentSize(theStrOrFile->ungetBuf) > 0)) + { + int currChar = 0; + // Technically, this returns NOTOK on error, but the error is underflow, which is + // checked above and so cannot happen. Therefore, safe to use sp_Pop() here. + sp_Pop(theStrOrFile->ungetBuf, currChar); + theChar = (char)currChar; + } + else if (theStrOrFile->pFile != NULL) + theChar = (char)getc(theStrOrFile->pFile); + else if (theStrOrFile->theStr != NULL && sb_GetUnreadCharCount(theStrOrFile->theStr) > 0) + { + theChar = sb_GetReadString(theStrOrFile->theStr) != NULL + ? sb_GetReadString(theStrOrFile->theStr)[0] + : EOF; + if (theChar != EOF) + sb_ReadSkipChar(theStrOrFile->theStr); + } + + return theChar; +} + +/******************************************************************** + sf_ReadSkipChar() + + Calls sf_getc() and does nothing with the returned character + ********************************************************************/ + +int sf_ReadSkipChar(strOrFileP theStrOrFile) +{ + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + if (sf_getc(theStrOrFile) == EOF) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ReadSkipWhitespace() + + Repeatedly calls sf_getc() to find the next non-space character + before hitting EOF + ********************************************************************/ + +int sf_ReadSkipWhitespace(strOrFileP theStrOrFile) +{ + char currChar = EOF; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + while ((currChar = sf_getc(theStrOrFile)) != EOF && isspace(currChar)) + { + continue; + } + + if (sf_ungetc(currChar, theStrOrFile) != currChar) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ReadSingleDigit() + + Calls sf_getc() and tests whether the character read corresponds to + a digit. + + Returns NOTOK if the character returned from sf_getc() is not a digit. + Assigns the digit read to the int * and returns OK upon success. + ********************************************************************/ + +int sf_ReadSingleDigit(int *digitToRead, strOrFileP theStrOrFile) +{ + int candidateDigit = EOF; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + candidateDigit = sf_getc(theStrOrFile); + if (!isdigit(candidateDigit)) + return NOTOK; + + // N.B. Subtract '0' = 48 to convert the digit + // char to the corresponding int + (*digitToRead) = candidateDigit - '0'; + return OK; +} + +/******************************************************************** + sf_ReadInteger() + + Repeatedly calls sf_getc() to obtain the characters corresponding to + an int, then parses that char* using sscanf() to extract the integer. + + Returns OK if successfully extracted the digits of and produced the + int from theStrOrFile, or NOTOK otherwise. + ********************************************************************/ + +int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile) +{ + int exitCode = OK; + + int intCandidate = 0, intCandidateIndex = 0; + char currChar = '\0', nextChar = '\0'; + bool startedReadingInt = FALSE, isNegative = FALSE; + char intCandidateStr[MAXCHARSFOR32BITINT + 1]; + memset(intCandidateStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char)); + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + do + { + currChar = sf_getc(theStrOrFile); + if (currChar == '-') + { + if (startedReadingInt) + { + exitCode = NOTOK; + break; + } + else + { + nextChar = sf_getc(theStrOrFile); + if (sf_ungetc(nextChar, theStrOrFile) != nextChar) + { + exitCode = NOTOK; + break; + } + + if (!isdigit(nextChar)) + { + exitCode = NOTOK; + break; + } + else + { + intCandidateStr[intCandidateIndex++] = currChar; + isNegative = TRUE; + } + } + } + else if (isdigit(currChar)) + { + intCandidateStr[intCandidateIndex++] = currChar; + startedReadingInt = TRUE; + } + else + { + if (sf_ungetc(currChar, theStrOrFile) != currChar) + exitCode = NOTOK; + break; + } + + if ( + (!isNegative && (intCandidateIndex == (MAXCHARSFOR32BITINT - 2))) || + (isNegative && (intCandidateIndex == (MAXCHARSFOR32BITINT - 1)))) + { + if (sscanf(intCandidateStr, "%d", &intCandidate) != 1) + exitCode = NOTOK; + else + { + nextChar = sf_getc(theStrOrFile); + if (isdigit(nextChar)) + { + // N.B. The only way we could be here is if `i` is less than the max possible + // length of the string representation of a signed 32-bit integer (10 for + // positive and 11 for negative) + int underflowThreshold = (INT32_MIN / 10), overflowThreshold = (INT32_MAX / 10); + if (isNegative) + { + if (intCandidate < underflowThreshold) + exitCode = NOTOK; + else if (intCandidate == underflowThreshold && nextChar == '9') + exitCode = NOTOK; + } + else + { + if (intCandidate > overflowThreshold) + exitCode = NOTOK; + else if (intCandidate == overflowThreshold && (nextChar == '8' || nextChar == '9')) + exitCode = NOTOK; + } + + if (exitCode == OK) + { + intCandidateStr[intCandidateIndex++] = nextChar; + } + } + else if (sf_ungetc(nextChar, theStrOrFile) != nextChar) + exitCode = NOTOK; + } + break; + } + } while (currChar != EOF); + + if (exitCode == OK) + { + if (sscanf(intCandidateStr, "%d", &intCandidate) != 1) + exitCode = NOTOK; + else + (*intToRead) = intCandidate; + } + + return exitCode; +} + +/******************************************************************** + sf_ReadSkipInteger() + + Calls sf_ReadInteger() and discards the result. + ********************************************************************/ + +int sf_ReadSkipInteger(strOrFileP theStrOrFile) +{ + int temp = 0; + + if (sf_ReadInteger(&temp, theStrOrFile) != OK) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ReadSkipLineRemainder() + + Calls sf_fgets() and discards the result. + ********************************************************************/ + +int sf_ReadSkipLineRemainder(strOrFileP theStrOrFile) +{ + char lineRemainderToSkip[MAXLINE + 1]; + memset(lineRemainderToSkip, '\0', (MAXLINE + 1)); + + if (sf_fgets(lineRemainderToSkip, MAXLINE, theStrOrFile) == NULL) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ungetc() + + Order of parameters matches stdio ungetc(). + + For both the case where the strOrFile contains a FILE * and the case + where it contains a strBufP, we unget to the ungetBuf; this ungetBuf + is consumed first when we sf_getc(), sf_fgets(), etc. + + Like ungetc() in stdio, on success theChar is returned. On failure, + EOF is returned. + ********************************************************************/ + +char sf_ungetc(char theChar, strOrFileP theStrOrFile) +{ + if (theChar == EOF || + sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER || + sp_GetCurrentSize(theStrOrFile->ungetBuf) >= sp_GetCapacity(theStrOrFile->ungetBuf)) + return EOF; // Acceptable downcast, allowing char rather than int return type + + sp_Push(theStrOrFile->ungetBuf, theChar); + return theChar; +} + +/******************************************************************** + sf_ungets() + + Pushes characters of strToUnget in reverse order to the ungetBuf so + that they can be fetched from the ungetBuf in the order of the + original string. + + Returns OK on success and NOTOK on failure. + ********************************************************************/ + +int sf_ungets(char *strToUnget, strOrFileP theStrOrFile) +{ + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER || + (int)strlen(strToUnget) > (sp_GetCapacity(theStrOrFile->ungetBuf) - sp_GetCurrentSize(theStrOrFile->ungetBuf))) + return NOTOK; + + for (int i = (strlen(strToUnget) - 1); i >= 0; i--) + sp_Push(theStrOrFile->ungetBuf, strToUnget[i]); + + return OK; +} + +/******************************************************************** + sf_fgets() + + Order of parameters matches stdio fgets(). + + First param is the string to populate (assumes allocated (count + 1) + bytes), second param is the max number of characters to read, and + third param is the pointer to the string-or-file container from which + we wish to read count characters (or up to and including \n). + + Like fgets() in stdio, this function doesn't check that enough memory + is allocated for str to contain count characters plus \0. + + Like fgets() in stdio, on success the pointer to the buffer is returned. + On failure, NULL is returned. + ********************************************************************/ + +char *sf_fgets(char *str, int count, strOrFileP theStrOrFile) +{ + int charsToReadFromUngetBuf = 0; + int charsToReadFromStrOrFile = count; + + if (str == NULL || count < 0 || + sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NULL; + + if (theStrOrFile->ungetBuf != NULL) + { + int numCharsInUngetBuf = sp_GetCurrentSize(theStrOrFile->ungetBuf); + if (numCharsInUngetBuf > 0) + { + char currChar = '\0'; + bool encounteredNewline = FALSE; + + charsToReadFromUngetBuf = (count > numCharsInUngetBuf) ? numCharsInUngetBuf : count; + for (int i = 0; i < charsToReadFromUngetBuf; i++) + { + currChar = sf_getc(theStrOrFile); + if (currChar == EOF) + return NULL; + str[i] = currChar; + str[i + 1] = '\0'; + // N.B. fgets() includes the \n in the string returned, and + // no further characters shall be read + if (currChar == '\n') + { + encounteredNewline = TRUE; + break; + } + } + // N.B. If we broke out of the loop early due to \n, do not read + // any further characters from stream + charsToReadFromStrOrFile = (encounteredNewline) ? 0 : ((count > numCharsInUngetBuf) ? (count - charsToReadFromUngetBuf) : 0); + } + } + + if (charsToReadFromStrOrFile > 0) + { + if (theStrOrFile->pFile != NULL) + { + // N.B. if fgets() returns NULL (can't read more characters) AND the ungetBuf was empty, + // then return NULL (error trying to read from empty stream). Otherwise, return str (that + // was read from ungetBuf) + if (fgets(str + charsToReadFromUngetBuf, charsToReadFromStrOrFile, theStrOrFile->pFile) == NULL) + { + if (charsToReadFromUngetBuf == 0) + return NULL; + } + } + else if (theStrOrFile->theStr != NULL) + { + char *theStrBuf = sb_GetReadString(theStrOrFile->theStr); + if (theStrBuf != NULL && sb_GetUnreadCharCount(theStrOrFile->theStr) > 0) + { + if (strncpy( + str + charsToReadFromUngetBuf, + theStrBuf, + charsToReadFromStrOrFile) == NULL) + return NULL; + + sb_SetReadPos(theStrOrFile->theStr, (sb_GetReadPos(theStrOrFile->theStr) + charsToReadFromStrOrFile)); + } + else if (charsToReadFromUngetBuf == 0) + return NULL; + } + } + + return str; +} + +/******************************************************************** + sf_fputs() + + Order of parameters matches stdio fputs(). + + First param is the string to append, and the second param is the + string-or-file container to which we wish to append. + + On success, returns the number of characters written. + On failure, returns EOF. + ********************************************************************/ + +int sf_fputs(char const *strToWrite, strOrFileP theStrOrFile) +{ + int outputLen = EOF; + + if (strToWrite == NULL || + sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != OUTPUT_CONTAINER) + return EOF; + + // N.B. fputs() will fail and return EOF if pFile doesn't correspond + // to an output stream + if (theStrOrFile->pFile != NULL) + outputLen = fputs(strToWrite, theStrOrFile->pFile); + else if (theStrOrFile->theStr != NULL) + { + if (sb_ConcatString(theStrOrFile->theStr, strToWrite) == OK) + outputLen = strlen(strToWrite); + else + outputLen = EOF; + } + + return outputLen; +} + +/******************************************************************** + sf_takeTheStr() + + Returns the char * stored in the string-or-file container and NULLs + out the internal reference so ownership of the memory is transferred + to the caller. + + The pointer returned will be NULL if the strOrFile contains a FILE *. + ********************************************************************/ + +char *sf_takeTheStr(strOrFileP theStrOrFile) +{ + char *theStr = NULL; + if (theStrOrFile->theStr != NULL) + { + theStr = sb_TakeString(theStrOrFile->theStr); + sb_Free((&theStrOrFile->theStr)); + theStrOrFile->theStr = NULL; + } + + return theStr; +} + +/******************************************************************** + sf_closeFile() + + If the strOrFile container contains a string, degenerately returns OK. + + If the strOrFile container contains a FILE pointer: + - if the FILE pointer is one of stdin, stdout, or stderr, calls + fflush() on the stream and captures the errorCode + - else, closes pFile and sets the internal pointer to NULL, then + captures the errorCode from fclose() + If the errorCode is less than 0, returns NOTOK, otherwise returns OK. + ********************************************************************/ + +int sf_closeFile(strOrFileP theStrOrFile) +{ + FILE *pFile = theStrOrFile->pFile; + theStrOrFile->pFile = NULL; + if (pFile != NULL) + { + int errorCode = 0; + + if (pFile == stdin || pFile == stdout || pFile == stderr) + errorCode = fflush(pFile); + else + errorCode = fclose(pFile); + + if (errorCode < 0) + return NOTOK; + } + + if (theStrOrFile->ungetBuf != NULL) + { + sp_Free(&(theStrOrFile->ungetBuf)); + } + theStrOrFile->ungetBuf = NULL; + + return OK; +} + +/******************************************************************** + sf_Free() + + Receives a pointer-pointer to a string-or-file container. + + If the strOrFile contains a string which has not yet been "taken" + using sf_takeTheStr() (i.e. we want inputStr to be freed, and in an + error state we want to free outputStr), the string is freed, the + internal pointer is set to NULL, and theStrPos is set to 0. + + If the strOrFile contains a FILE pointer, we call sf_closeFile() + and set the internal pointer to NULL. Note that unless we are in an + error state when sf_Free() is called, sf_closeFile should have already + been called. + + Finally, we use the indirection operator to free the strOrFile + container and set the pointer to NULL. + ********************************************************************/ + +void sf_Free(strOrFileP *pStrOrFile) +{ + if (pStrOrFile != NULL && (*pStrOrFile) != NULL) + { + if ((*pStrOrFile)->theStr != NULL) + sb_Free((&(*pStrOrFile)->theStr)); + (*pStrOrFile)->theStr = NULL; + + // TODO: (#56) if the strOrFile container's FILE pointer + // corresponds to an output file, i.e. ioMode is 'w', + // we should try to remove the file since the error state + // means the contents are invalid + if ((*pStrOrFile)->pFile != NULL) + sf_closeFile((*pStrOrFile)); + (*pStrOrFile)->pFile = NULL; + + if ((*pStrOrFile)->ungetBuf != NULL) + { + sp_Free(&((*pStrOrFile)->ungetBuf)); + } + (*pStrOrFile)->ungetBuf = NULL; + + free(*pStrOrFile); + (*pStrOrFile) = NULL; + } +} diff --git a/planarity_src/c/graphLib/io/strOrFile.h b/planarity_src/c/graphLib/io/strOrFile.h new file mode 100644 index 0000000..fb26eb5 --- /dev/null +++ b/planarity_src/c/graphLib/io/strOrFile.h @@ -0,0 +1,61 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef STR_OR_FILE_H +#define STR_OR_FILE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include "../lowLevelUtils/stack.h" +#include "strbuf.h" + +#define INPUT_CONTAINER 1 +#define OUTPUT_CONTAINER 2 + typedef struct + { + strBufP theStr; + FILE *pFile; + int containerType; + stackP ungetBuf; + + } strOrFile; + + typedef strOrFile *strOrFileP; + + strOrFileP sf_New(char const *theStr, char const *fileName, char const *ioMode); + int sf_ValidateStrOrFile(strOrFileP theStrOrFile); + + char sf_getc(strOrFileP theStrOrFile); + int sf_ReadSkipChar(strOrFileP theStrOrFile); + int sf_ReadSkipWhitespace(strOrFileP theStrOrFile); + int sf_ReadSingleDigit(int *digitToRead, strOrFileP theStrOrFile); + int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile); + int sf_ReadSkipInteger(strOrFileP theStrOrFile); + int sf_ReadSkipLineRemainder(strOrFileP theStrOrFile); + + char sf_ungetc(char theChar, strOrFileP theStrOrFile); + int sf_ungets(char *contentsToUnget, strOrFileP theStrOrFile); + + char *sf_fgets(char *str, int count, strOrFileP theStrOrFile); + + int sf_fputs(char const *strToWrite, strOrFileP theStrOrFile); + + char *sf_takeTheStr(strOrFileP theStrOrFile); + + int sf_closeFile(strOrFileP theStrOrFile); + + void sf_Free(strOrFileP *pStrOrFile); + +#ifdef __cplusplus +} +#endif + +#endif /* STR_OR_FILE_H */ diff --git a/planarity_src/c/graphLib/io/strbuf.c b/planarity_src/c/graphLib/io/strbuf.c new file mode 100644 index 0000000..efe07f6 --- /dev/null +++ b/planarity_src/c/graphLib/io/strbuf.c @@ -0,0 +1,247 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../lowLevelUtils/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 const*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; +} diff --git a/planarity_src/c/graphLib/io/strbuf.h b/planarity_src/c/graphLib/io/strbuf.h new file mode 100644 index 0000000..f9a504d --- /dev/null +++ b/planarity_src/c/graphLib/io/strbuf.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 1997-2025, 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_GetUnreadCharCount(theStrBuf) (theStrBuf->size - theStrBuf->readPos) +#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 const*); + 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 */ diff --git a/planarity_src/c/graphLib/lowLevelUtils/apiutils.c b/planarity_src/c/graphLib/lowLevelUtils/apiutils.c new file mode 100644 index 0000000..23ea81b --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/apiutils.c @@ -0,0 +1,73 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include +#include +#include + +#include "apiutils.h" +#include "appconst.h" + +int quietMode = FALSE; + +int getQuietModeSetting(void) +{ + return quietMode; +} + +void setQuietModeSetting(int newQuietModeSetting) +{ + quietMode = newQuietModeSetting; +} + +void Message(char const *message) +{ + if (!getQuietModeSetting()) + { + fprintf(stdout, "%s", message); + fflush(stdout); + } +} + +void ErrorMessage(char const *message) +{ + if (!getQuietModeSetting()) + { + fprintf(stderr, "%s", message); + fflush(stderr); + } +} + +int GetNumCharsToReprInt(int theNum, int *numCharsRequired) +{ + int charCount = 0; + + if (numCharsRequired == NULL) + return NOTOK; + + if (theNum < 0) + { + charCount++; + // N.B. since 32-bit signed integers are represented using twos-complement, + // the absolute value of INT_MIN is not defined; however, adding 1 to this + // min value before taking the absolute value will still require the same + // number of digits. + if ((theNum == INT_MIN) || (theNum == INT8_MAX) || (theNum == INT16_MIN) || (theNum == INT32_MIN)) + theNum++; + theNum = abs(theNum); + } + + while (theNum > 0) + { + theNum /= 10; + charCount++; + } + + (*numCharsRequired) = charCount; + + return OK; +} diff --git a/planarity_src/c/graphLib/lowLevelUtils/apiutils.h b/planarity_src/c/graphLib/lowLevelUtils/apiutils.h new file mode 100644 index 0000000..f749a7e --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/apiutils.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ +#ifndef APIUTILS_H +#define APIUTILS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "stdio.h" + +// N.B. Every time this is used to create a string for a message or +// error message, the developer must check that there will not be a +// memory overwrite error. +#define MAXLINE 1024 + +// N.B. Every time you're trying to read a 32-bit int from a string, +// you should only need to read this many characters: an optional '-', +// followed by 10 digits (max signed 32-bit int value is 2,147,483,647). +// One must always allocate an additional byte for the null-terminator! +#define MAXCHARSFOR32BITINT 11 + + extern int quietMode; + + extern int getQuietModeSetting(void); + extern void setQuietModeSetting(int); + + extern void Message(char const*message); + extern void ErrorMessage(char const*message); + + int GetNumCharsToReprInt(int theNum, int *numCharsRequired); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/planarity_src/c/graphLib/lowLevelUtils/appconst.h b/planarity_src/c/graphLib/lowLevelUtils/appconst.h new file mode 100644 index 0000000..d813c9b --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/appconst.h @@ -0,0 +1,103 @@ +#ifndef APPCONST_H +#define APPCONST_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "apiutils.h" + +// NOTE: This is defined on 32- and 64-bit Windows systems; see +// https://sourceforge.net/p/predef/wiki/OperatingSystems +#if defined(WIN32) || defined(_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(void); +#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 */ +#define USE_FASTER_1BASEDARRAYS + +#ifdef USE_0BASEDARRAYS +#undef USE_FASTER_1BASEDARRAYS +#endif + +#ifdef USE_FASTER_1BASEDARRAYS +// This definition is used with 1-based array indexing +#define NIL 0 +#define NIL_CHAR 0x00 +#else +// This definition is used with 0-based array indexing +#define NIL -1 +#define NIL_CHAR 0xFF +#endif + +/******************************************************************** + A few simple integer selection macros + ********************************************************************/ + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +#define MIN3(x, y, z) MIN(MIN((x), (y)), MIN((y), (z))) +#define MAX3(x, y, z) MAX(MAX((x), (y)), MAX((y), (z))) + +#endif diff --git a/planarity_src/c/listcoll.c b/planarity_src/c/graphLib/lowLevelUtils/listcoll.c similarity index 58% rename from planarity_src/c/listcoll.c rename to planarity_src/c/graphLib/lowLevelUtils/listcoll.c index 1ba0a12..e530d36 100644 --- a/planarity_src/c/listcoll.c +++ b/planarity_src/c/graphLib/lowLevelUtils/listcoll.c @@ -1,315 +1,278 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 +/* +Copyright (c) 1997-2025, 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 successor 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 diff --git a/planarity_src/c/graphLib/lowLevelUtils/listcoll.h b/planarity_src/c/graphLib/lowLevelUtils/listcoll.h new file mode 100644 index 0000000..c4ea3be --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/listcoll.h @@ -0,0 +1,122 @@ +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/lowLevelUtils/platformTime.h b/planarity_src/c/graphLib/lowLevelUtils/platformTime.h new file mode 100644 index 0000000..91c2ec1 --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/platformTime.h @@ -0,0 +1,54 @@ +#ifndef PLATFORM_TIME +#define PLATFORM_TIME + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +// NOTE: Since platformTime.h is only #include'd by appconst.h, and since appconst.h +// #define's WINDOWS before including platformTime.h, we condition on WINDOWS being defined. +#ifdef WINDOWS + +#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 diff --git a/planarity_src/c/graphLib/lowLevelUtils/stack.c b/planarity_src/c/graphLib/lowLevelUtils/stack.c new file mode 100644 index 0000000..fc31bde --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/stack.c @@ -0,0 +1,224 @@ +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/lowLevelUtils/stack.h b/planarity_src/c/graphLib/lowLevelUtils/stack.h new file mode 100644 index 0000000..67db50d --- /dev/null +++ b/planarity_src/c/graphLib/lowLevelUtils/stack.h @@ -0,0 +1,142 @@ +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphDrawPlanar.c b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.c similarity index 74% rename from planarity_src/c/graphDrawPlanar.c rename to planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.c index b87afea..25bf647 100644 --- a/planarity_src/c/graphDrawPlanar.c +++ b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.c @@ -1,1082 +1,1067 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graphDrawPlanar.h" -#include "graphDrawPlanar.private.h" - -extern int DRAWPLANAR_ID; - -#include "graph.h" - -#include -#include -#include - -extern void _ClearVisitedFlags(graphP theGraph); - -/* Private functions exported to system */ - -void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink); -int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink); - -int _ComputeVisibilityRepresentation(DrawPlanarContext *context); -int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context); - -/* Private functions */ -int _ComputeVertexPositions(DrawPlanarContext *context); -int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pIndex); -int _ComputeEdgePositions(DrawPlanarContext *context); -int _ComputeVertexRanges(DrawPlanarContext *context); -int _ComputeEdgeRanges(DrawPlanarContext *context); - -/******************************************************************** - _ComputeVisibilityRepresentation() - - Compute vertex positions - Compute edge positions - Assign horizontal ranges of vertices - Assign vertical ranges of edges - - ********************************************************************/ - -int _ComputeVisibilityRepresentation(DrawPlanarContext *context) -{ - if (sp_NonEmpty(context->theGraph->edgeHoles)) - return NOTOK; - - if (_ComputeVertexPositions(context) != OK) - return NOTOK; - - if (_ComputeEdgePositions(context) != OK) - return NOTOK; - - if (_ComputeVertexRanges(context) != OK) - return NOTOK; - - if (_ComputeEdgeRanges(context) != OK) - return NOTOK; - - return OK; -} - -/******************************************************************** - _ComputeVertexPositions() - - Computes the vertex positions in the graph. This method accounts - for disconnected graphs by finding the DFS tree roots and then, - for each, invoking _ComputeVertexPositionsInComponent(). - The index variable for the positioning is maintained by this method - so that the vertices in separate components still get distinct - vertex positions. - ********************************************************************/ - -int _ComputeVertexPositions(DrawPlanarContext *context) -{ - graphP theEmbedding = context->theGraph; - int v, vertpos; - - vertpos = 0; - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - // For each DFS tree root in the embedding, we - // compute the vertex positions - if (gp_IsDFSTreeRoot(theEmbedding, v)) - { - if (_ComputeVertexPositionsInComponent(context, v, &vertpos) != OK) - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - _ComputeVertexPositionsInComponent() - - The vertical positions of the vertices are computed based in part - on the information compiled during the planar embedding. - - Each vertex is marked as being between its parent and some ancestor - or beyond the parent relative to the ancestor. The localized, - intuitive notion is that the vertex is either below the parent - or above the parent, but the bicomp containing the vertex, its - parent and the ancestor may be turned upside-down as the result - of a global sequence of operations, resulting in a between or beyond - generalization. - - As the core planarity algorithm constructs successively larger - bicomps out of smaller ones, the bicomp root and its DFS child - are marked as 'tied' in vertex position using markers along the - external face. The marking of the DFS child may be indirect. - Since the child may not be on the external face, its descendant - that is next along the external face is marked instead. - - Later (possibly in the same step or possibly many vertices later), - the Walkdown proceeds around the bicomp and returns to each merge - point, and the tie is broken based on the direction of approach. - - As the Walkdown passes a vertex to its successor, the external - face is short-circuited to remove the vertex from it. Immediately - before this occurs, the new drawing method resolves the tie. Since - the vertex is going to the internal face, its vertex position should - be 'between' its successor and the current vertex being processed - by the Walkdown. - - If the vertex is a child of its external face successor, then it - is simply marked as being 'between' that successor and the current - vertex being processed by the planarity method. But if the vertex - is the parent of its external face successor, then the successor - is placed 'beyond' the vertex. Recall that the successor is either - the DFS child of the vertex or a descendant of that DFS child that - was specially marked because it, not the DFS child, was on the - external face. - - This explains the information that has been collected by the - planarity embedder, which will now be turned into a vertex ordering - system. The idea is to proceed with a pre-order traversal of - the DFS tree, determining the relative orders of the ancestors of - a vertex by the time we get to a vertex. This will allow us to - convert between/beyond into above/below based on the known relative - order of the parent and some given ancestor of the vertex. A vertex - would then be added immediately above or below its parent in the - total ordering, and then the algorithm proceeds to the descendants. - - Consider a depth-first pre-order visitation of vertices. If the - full order of all vertices visited so far is dynamically maintained, - then it is easy to decide whether a vertex goes above or below - its parent based on the between/beyond indicator and the relative - positions in the order of the parent and given ancestor of the - vertex. If the ancestor is above the parent, then 'between' means - put the vertex immediately above its parent and 'beyond' means put - the vertex immediately below its parent in the order. And if the - ancestor is below the parent, then the meaning of between and - beyond are simply reversed. - - Once a vertex is known to be above or below its parent, the drawing - flag is changed from between/beyond to above/below, and processing - proceeds to the next vertex in pre-order depth first search. - - The difficulty lies in keeping an up-to-date topological ordering - that can be queried in constant time to find the relative positions - of two vertices. By itself, this is an instance of "online" or - dynamic topological sorting and has been proven not to be achievable - in linear total time. But this is a special case of the problem and - is therefore solvable through the collection and maintenance of some - additional information. - - Recall that the ancestor V of a vertex is recorded when the setting - for between/beyond is made for a vertex. However, the Walkdown is - invoked on the bicomp rooted by edge (V', C), so the child C of V - that roots the subtree containing the vertex being marked is known. - - Note that when a DFS child is placed above its parent, the entire - DFS subtree of vertices is placed above the parent. Hence, to - determine whether the parent P of a vertex W is above the ancestor - V, where W is marked either between or beyond P and V, we need - only determine the relationship between V and C, which has already - been directly determined due to previous steps of the algorithm - (because V and C are ancestors of P and W). If C is above/below V - then so is P. - - As mentioned above, once the position of P is known relative to V, - it is a simple matter to decide whether to put W above or below P - based on the between/beyond indicator stored in W during embedding. - ********************************************************************/ - -int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pVertpos) -{ -graphP theEmbedding = context->theGraph; -listCollectionP theOrder = LCNew(gp_PrimaryVertexIndexBound(theEmbedding)); -int W, P, C, V, e; - - if (theOrder == NULL) - return NOTOK; - - // Determine the vertex order using a depth first search with - // pre-order visitation. - - sp_ClearStack(theEmbedding->theStack); - sp_Push(theEmbedding->theStack, root); - while (!sp_IsEmpty(theEmbedding->theStack)) - { - sp_Pop(theEmbedding->theStack, W); - - P = gp_GetVertexParent(theEmbedding, W); - V = context->VI[W].ancestor; - C = context->VI[W].ancestorChild; - - // For the special case that we just popped the DFS tree root, - // we simply add the root to its own position. - if (gp_IsNotVertex(P)) - { - // Put the DFS root in the list by itself - LCAppend(theOrder, NIL, W); - // The children of the DFS root have the root as their - // ancestorChild and 'beyond' as the drawingFlag, so this - // causes the root's children to be placed below the root - context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; - } - - // Determine vertex W position relative to P - else - { - // An unresolved tie is an error - if (context->VI[W].drawingFlag == DRAWINGFLAG_TIE) - return NOTOK; - - // If W is the child of a DFS root, then there is no vertex C - // between it and some ancestor V. Both V and C are not a vertex, - // and W will simply take the default of being below its parent. - // If C is a vertex, then it has already been absolutely positioned - // and can be used to help position W relative to its parent P, - // which is equal to or descendant to C. If C below V, then P below V, - // so interpret 'W between P and V' as 'W above P', and interpret - // 'W beyond P relative to V' as 'W below P'. - if (gp_IsNotVertex(C) || context->VI[C].drawingFlag == DRAWINGFLAG_BELOW) - { - if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN) - context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE; - else - context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; - } - - // If C above V, then P above V, so interpret W between - // P and V as W below P, and interpret W beyond P relative - // to V as W above P. - else - { - if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN) - context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; - else - context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE; - } - - if (context->VI[W].drawingFlag == DRAWINGFLAG_BELOW) - LCInsertAfter(theOrder, P, W); - else - LCInsertBefore(theOrder, P, W); - } - - // Push DFS children - e = gp_GetFirstArc(theEmbedding, W); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theEmbedding, e) == EDGE_TYPE_CHILD) - sp_Push(theEmbedding->theStack, gp_GetNeighbor(theEmbedding, e)); - - e = gp_GetNextArc(theEmbedding, e); - } - } - - // Use the order to assign vertical positions - V = root; - while (gp_IsVertex(V)) - { - context->VI[V].pos = *pVertpos; - (*pVertpos)++; - V = LCGetNext(theOrder, root, V); - } - - // Clean up and return - - LCFree(&theOrder); - return OK; -} - - -#ifdef LOGGING -/******************************************************************** - _LogEdgeList() - Used to show the progressive calculation of the edge position list. - ********************************************************************/ -void _LogEdgeList(graphP theEmbedding, listCollectionP edgeList, int edgeListHead) -{ - int eIndex = edgeListHead, e, eTwin; - - gp_Log("EdgeList: [ "); - - while (gp_IsArc(eIndex)) - { - e = (eIndex << 1); - eTwin = gp_GetTwinArc(theEmbedding, e); - - gp_Log(gp_MakeLogStr2("(%d, %d) ", - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eTwin)))); - - eIndex = LCGetNext(edgeList, edgeListHead, eIndex); - } - - gp_LogLine("]"); -} -#endif - -/******************************************************************** - _ComputeEdgePositions() - - Performs a vertical sweep of the combinatorial planar embedding, - developing the edge order in the horizontal sweep line as it - advances through the vertices according to their assigned - vertical positions. - - The 'visitedInfo' member of each vertex is used to indicate the - location in the edge order list of the generator edge for the vertex. - The generator edge is the first edge used to visit the vertex from - a higher vertex in the drawing (i.e. a vertex with an earlier, or - lower, position number). - - All edges added from this vertex to the neighbors below it are - added immediately after the generator edge for the vertex. - ********************************************************************/ - -int _ComputeEdgePositions(DrawPlanarContext *context) -{ -graphP theEmbedding = context->theGraph; -int *vertexOrder = NULL; -listCollectionP edgeList = NULL; -int edgeListHead, edgeListInsertPoint; -int e, eTwin, eCur, v, vpos, epos, eIndex; - - gp_LogLine("\ngraphDrawPlanar.c/_ComputeEdgePositions() start"); - - // Sort the vertices by vertical position (in linear time) - - if ((vertexOrder = (int *) malloc(theEmbedding->N * sizeof(int))) == NULL) - return NOTOK; - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - vertexOrder[context->VI[v].pos] = v; - - // Allocate the edge list of size M. - // This is an array of (prev, next) pointers. - // An edge at position X corresponds to the edge - // at position X in the graph structure, which is - // represented by a pair of adjacent edge records - // at index 2X. - - if (theEmbedding->M > 0 && (edgeList = LCNew(gp_GetFirstEdge(theEmbedding)/2+theEmbedding->M)) == NULL) - { - free(vertexOrder); - return NOTOK; - } - - edgeListHead = NIL; - - // Each vertex starts out with a NIL generator edge. - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - gp_SetVertexVisitedInfo(theEmbedding, v, NIL); - - // Perform the vertical sweep of the combinatorial embedding, using - // the vertex ordering to guide the sweep. - // For each vertex, each edge leading to a vertex with a higher number in - // the vertex order is recorded as the "generator edge", or the edge of - // first discovery of that higher numbered vertex, unless the vertex already has - // a recorded generator edge - for (vpos = 0; vpos < theEmbedding->N; vpos++) - { - // Get the vertex associated with the position - v = vertexOrder[vpos]; - gp_LogLine(gp_MakeLogStr3("Processing vertex %d with DFI=%d at position=%d", - gp_GetVertexIndex(theEmbedding, v), v, vpos)); - - // The DFS tree root of a connected component is always the least - // number vertex in the vertex ordering. We have to give it a - // false generator edge so that it is still "visited" and then - // all of its edges are generators for its neighbor vertices because - // they all have greater numbers in the vertex order. - if (gp_IsDFSTreeRoot(theEmbedding, v)) - { - // Set a false generator edge, so the vertex is distinguishable from - // a vertex with no generator edge when its neighbors are visited - // This way, an edge from a neighbor won't get recorded as the - // generator edge of the DFS tree root. - gp_SetVertexVisitedInfo(theEmbedding, v, NIL - 1); - - // Now we traverse the adjacency list of the DFS tree root and - // record each edge as the generator edge of the neighbors - e = gp_GetFirstArc(theEmbedding, v); - while (gp_IsArc(e)) - { - eIndex = (e >> 1); // div by 2 since each edge is a pair of arcs - - edgeListHead = LCAppend(edgeList, edgeListHead, eIndex); - gp_LogLine(gp_MakeLogStr2("Append generator edge (%d, %d) to edgeList", - gp_GetVertexIndex(theEmbedding, v), gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)))); - - // Set the generator edge for the root's neighbor - gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, e), e); - - // Go to the next node of the root's adj list - e = gp_GetNextArc(theEmbedding, e); - } - } - - // Else, if we are not on a DFS tree root... - else - { - // Get the generator edge of the vertex - // Note that this never gets the false generator edge of a DFS tree root - eTwin = gp_GetVertexVisitedInfo(theEmbedding, v); - if (gp_IsNotArc(eTwin)) - return NOTOK; - e = gp_GetTwinArc(theEmbedding, eTwin); - - // Traverse the edges of the vertex, starting - // from the generator edge and going counterclockwise... - - eIndex = (e >> 1); - edgeListInsertPoint = eIndex; - - eCur = gp_GetNextArcCircular(theEmbedding, e); - while (eCur != e) - { - // If the neighboring vertex's position is greater - // than the current vertex (meaning it is lower in the - // diagram), then add that edge to the edge order. - - if (context->VI[gp_GetNeighbor(theEmbedding, eCur)].pos > vpos) - { - eIndex = eCur >> 1; - LCInsertAfter(edgeList, edgeListInsertPoint, eIndex); - - gp_LogLine(gp_MakeLogStr4("Insert (%d, %d) after (%d, %d)", - gp_GetVertexIndex(theEmbedding, v), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)))); - - edgeListInsertPoint = eIndex; - - // If the vertex does not yet have a generator edge, then set it. - // Note that a DFS tree root has a false generator edge, so this if - // test avoids setting a generator edge for a DFS tree root - if (gp_IsNotArc(gp_GetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)))) - { - gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur), eCur); - gp_LogLine(gp_MakeLogStr2("Generator edge (%d, %d)", - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)))); - } - } - - // Go to the next node in v's adjacency list - eCur = gp_GetNextArcCircular(theEmbedding, eCur); - } - } - -#ifdef LOGGING - _LogEdgeList(theEmbedding, edgeList, edgeListHead); -#endif - } - - // Now iterate through the edgeList and assign positions to the edges. - epos = 0; - eIndex = edgeListHead; - while (gp_IsArc(eIndex)) - { - e = (eIndex << 1); - eTwin = gp_GetTwinArc(theEmbedding, e); - - context->E[e].pos = context->E[eTwin].pos = epos; - - epos++; - - eIndex = LCGetNext(edgeList, edgeListHead, eIndex); - } - - // Clean up and return - LCFree(&edgeList); - free(vertexOrder); - - gp_LogLine("graphDrawPlanar.c/_ComputeEdgePositions() end\n"); - - return OK; -} - -/******************************************************************** - _ComputeVertexRanges() - - Assumes edge positions are known (see _ComputeEdgePositions()). - A vertex spans horizontally the positions of the edges incident - to it. - ********************************************************************/ - -int _ComputeVertexRanges(DrawPlanarContext *context) -{ - graphP theEmbedding = context->theGraph; - int v, e, min, max; - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - min = theEmbedding->M + 1; - max = -1; - - // Iterate the edges, except in the isolated vertex case we just - // set the min and max to 1 since there no edges controlling where - // it gets drawn. - e = gp_GetFirstArc(theEmbedding, v); - if (gp_IsNotArc(e)) - { - min = max = 0; - } - else - { - while (gp_IsArc(e)) - { - if (min > context->E[e].pos) - min = context->E[e].pos; - - if (max < context->E[e].pos) - max = context->E[e].pos; - - e = gp_GetNextArc(theEmbedding, e); - } - } - - context->VI[v].start = min; - context->VI[v].end = max; - } - - return OK; -} - -/******************************************************************** - _ComputeEdgeRanges() - - Assumes vertex positions are known (see _ComputeVertexPositions()). - An edges spans the vertical range of its endpoints. - ********************************************************************/ - -int _ComputeEdgeRanges(DrawPlanarContext *context) -{ - graphP theEmbedding = context->theGraph; - int e, eTwin, EsizeOccupied, v1, v2, pos1, pos2; - - // Deleted edges are not supported, nor should they be in the embedding, so - // this is just a reality check that avoids an in-use test inside the loop - if (sp_NonEmpty(theEmbedding->edgeHoles)) - return NOTOK; - - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - eTwin = gp_GetTwinArc(theEmbedding, e); - - v1 = gp_GetNeighbor(theEmbedding, e); - v2 = gp_GetNeighbor(theEmbedding, eTwin); - - pos1 = context->VI[v1].pos; - pos2 = context->VI[v2].pos; - - if (pos1 < pos2) - { - context->E[e].start = pos1; - context->E[e].end = pos2; - } - else - { - context->E[e].start = pos2; - context->E[e].end = pos1; - } - - context->E[eTwin].start = context->E[e].start; - context->E[eTwin].end = context->E[e].end; - } - - return OK; -} - -/******************************************************************** - _GetNextExternalFaceVertex() - Uses the extFace links to traverse to the next vertex on the external - face given a current vertex and the link that points to its predecessor. - ********************************************************************/ -int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink) -{ - int nextVertex = gp_GetExtFaceVertex(theGraph, curVertex, 1 ^ *pPrevLink); - - // If the two links in the new vertex are not equal, then only one points - // back to the current vertex, and it is the new prev link. - // Otherwise, the vertex is in a consistently oriented single-edge bicomp, so - // no adjustment of the prev link is needed (due to the consistent orientation). - if (gp_GetExtFaceVertex(theGraph, nextVertex, 0) != gp_GetExtFaceVertex(theGraph, nextVertex, 1)) - { - *pPrevLink = gp_GetExtFaceVertex(theGraph, nextVertex, 0)==curVertex ? 0 : 1; - } - - return nextVertex; -} - -/******************************************************************** - _CollectDrawingData() - To be called by core planarity Walkdown immediately before merging - bicomps and embedding a new back edge. - - Each bicomp is rooted by a DFS tree edge. The parent vertex in - that edge is the bicomp root, and the bicomp contains one DFS child - of the vertex, which is on the child end of the 'root edge'. - - Here we decide whether the DFS child is to be embedded between or - beyond its parent relative to vertex v, the one currently being - processed (and the ancestor endpoint of a back edge being embedded, - where the descendant endpoint is also an endpoint of the bicomp - root being merged). - ********************************************************************/ - -void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink) -{ -graphP theEmbedding = context->theGraph; -int K, Parent, BicompRoot, DFSChild, direction, descendant; - - gp_LogLine("\ngraphDrawPlanar.c/_CollectDrawingData() start"); - gp_LogLine(gp_MakeLogStr3("_CollectDrawingData(RootVertex=%d, W=%d, W_in=%d)", - RootVertex, W, WPrevLink)); - - /* Process all of the merge points to set their drawing flags. */ - - for (K = 0; K < sp_GetCurrentSize(theEmbedding->theStack); K += 4) - { - /* Get the parent and child that are about to be merged from - the 4-tuple in the merge stack */ - Parent = theEmbedding->theStack->S[K]; - BicompRoot = theEmbedding->theStack->S[K+2]; - DFSChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot); - - /* We get the active descendant vertex in the child bicomp that - will be adjacent to the parent along the external face. - This vertex is guaranteed to be found in one step - due to external face 'short-circuiting' that was done in - step 'Parent' of the planarity algorithm. - We pass theEmbedding->N for the second parameter because - of this; we use this function to signify need of extFace - links in the other implementation.*/ - - direction = theEmbedding->theStack->S[K+3]; - descendant = _GetNextExternalFaceVertex(theEmbedding, BicompRoot, &direction); - - /* Now we set the tie flag in the DFS child, and mark the - descendant and parent with non-NIL pointers to the child - whose tie flag is to be resolved as soon as one of the - two is connected to by an edge or child bicomp merge. */ - - context->VI[DFSChild].drawingFlag = DRAWINGFLAG_TIE; - - context->VI[descendant].tie[direction] = DFSChild; - - direction = theEmbedding->theStack->S[K+1]; - context->VI[Parent].tie[direction] = DFSChild; - - gp_LogLine(gp_MakeLogStr5("V[Parent=%d]=.tie[%d] = V[descendant=%d].tie[%d] = (child=%d)", - Parent, direction, descendant, theEmbedding->theStack->S[K+3], DFSChild)); - } - - gp_LogLine("graphDrawPlanar.c/_CollectDrawingData() end\n"); -} - -/******************************************************************** - _BreakTie() - - The given vertex W has just been arrived at by the core planarity - algorithm. Using WPrevLink, we seek its predecessor WPred on the - external face and test whether the two are involved in a tie that - can be resolved. - - Since the planarity algorithm has just passed by WPred, it is - safe to conclude that WPred can go between W and the current vertex. - - Of course, if W was the parent to some DFS child whose subtree - contains WPred, then the DFS child is marked 'between', placing - the whole subtree including WPred between W and the current vertex. - On the other hand, if WPred was the parent of some DFS child whose - subtree contained W, then we achieve the same effect of putting WPred - 'between' W and the curent vertex by marking the DFS child 'beyond'. - Since the DFS child and hence W are beyond W relative to the current - vertex, WPred is also between W and the current vertex. - - Thus the certain positional relationship between W and WPred - relative to a specific ancestor, the current vertex, is used to - indirectly break the positional tie between MIN(W, WPred) and the - DFS child of MIN(W, WPred) whose subtree contains MAX(W, WPred). - - The ancestorChild is the DFS child of the current vertex whose DFS - subtree contains W and WPred, and it is recorded here in order to - optimize the post-processing calculation of vertex positions. - ********************************************************************/ - -int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink) -{ -graphP theEmbedding = context->theGraph; - - /* First we get the predecessor of W. */ - -int WPredNextLink = 1^WPrevLink, - WPred = _GetNextExternalFaceVertex(theEmbedding, W, &WPredNextLink); - - gp_LogLine("\ngraphDrawPlanar.c/::_BreakTie() start"); - gp_LogLine(gp_MakeLogStr4("_BreakTie(BicompRoot=%d, W=%d, W_in=%d) WPred=%d", - BicompRoot, W, WPrevLink, WPred)); - - /* Ties happen only within a bicomp (i.e. between two non-root vertices) */ - if (gp_IsVirtualVertex(theEmbedding, W) || gp_IsVirtualVertex(theEmbedding, WPred)) - { - gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n"); - return OK; - } - - /* The two vertices are either tied or not; having one tied and the other - not is an error */ - - if (context->VI[W].tie[WPrevLink] != context->VI[WPred].tie[WPredNextLink]) - return NOTOK; - - /* If there is a tie, it can now be resolved. */ - if (gp_IsVertex(context->VI[W].tie[WPrevLink])) - { - int DFSChild = context->VI[W].tie[WPrevLink]; - - /* Set the two ancestor variables that contextualize putting W 'between' - or 'beyond' its parent relative to what. */ - - context->VI[DFSChild].ancestorChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot); - context->VI[DFSChild].ancestor = gp_GetPrimaryVertexFromRoot(theEmbedding, BicompRoot); - - gp_LogLine(gp_MakeLogStr4("V[child=%d]=.ancestorChild = %d, V[child=%d]=.ancestor = %d", - DFSChild, context->VI[DFSChild].ancestorChild, DFSChild, context->VI[DFSChild].ancestor)); - - /* If W is the ancestor of WPred, then the DFSChild subtree contains - WPred, and so must go between W and some ancestor. */ - if (W < WPred) - { - context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BETWEEN; - gp_LogLine(gp_MakeLogStr3("Child=%d is 'between' ancestorChild=%d and ancestor=%d", - DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor)); - } - - /* If W is the descendant, so we achieve the effect of putting WPred - between DFSChild and ancestor by putting the DFSChild 'beyond' WPred. */ - else - { - context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BEYOND; - gp_LogLine(gp_MakeLogStr3("Child=%d is 'beyond' ancestorChild=%d relative to ancestor=%d", - DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor)); - } - - /* The tie is resolved so clear the flags*/ - context->VI[W].tie[WPrevLink] = NIL; - context->VI[WPred].tie[WPredNextLink] = NIL; - } - - gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n"); - return OK; -} - -/******************************************************************** - _RenderToString() - Draws the previously calculated visibility representation in a - string of size (M+1)*2N + 1 characters, which should be deallocated - with free(). - - Returns NULL on failure, or the string containing the visibility - representation otherwise. The string can be printed using %s, - ********************************************************************/ - -char *_RenderToString(graphP theEmbedding) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theEmbedding, DRAWPLANAR_ID, (void *) &context); - - if (context != NULL) - { - int N = theEmbedding->N; - int M = theEmbedding->M; - int zeroBasedVertexOffset = (theEmbedding->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theEmbedding) : 0; - int n, m, EsizeOccupied, v, vRange, e, eRange, Mid, Pos; - char *visRep = (char *) malloc(sizeof(char) * ((M+1) * 2*N + 1)); - char numBuffer[32]; - - if (visRep == NULL) - return NULL; - - if (sp_NonEmpty(context->theGraph->edgeHoles)) - { - free(visRep); - return NULL; - } - - // Clear the space - for (n = 0; n < N; n++) - { - for (m=0; m < M; m++) - { - visRep[(2*n) * (M+1) + m] = ' '; - visRep[(2*n+1) * (M+1) + m] = ' '; - } - - visRep[(2*n) * (M+1) + M] = '\n'; - visRep[(2*n+1) * (M+1) + M] = '\n'; - } - - // Draw the vertices - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - Pos = context->VI[v].pos; - for (vRange=context->VI[v].start; vRange <= context->VI[v].end; vRange++) - visRep[(2*Pos) * (M+1) + vRange] = '-'; - - // Draw vertex label - Mid = (context->VI[v].start + context->VI[v].end) / 2; - sprintf(numBuffer, "%d", v - zeroBasedVertexOffset); - if ((unsigned)(context->VI[v].end - context->VI[v].start + 1) >= strlen(numBuffer)) - { - strncpy(visRep + (2*Pos) * (M+1) + Mid, numBuffer, strlen(numBuffer)); - } - // If the vertex width is less than the label width, then fail gracefully - else - { - if (strlen(numBuffer)==2) - visRep[(2*Pos) * (M+1) + Mid] = numBuffer[0]; - else - visRep[(2*Pos) * (M+1) + Mid] = '*'; - - visRep[(2*Pos+1) * (M+1) + Mid] = numBuffer[strlen(numBuffer)-1]; - } - } - - // Draw the edges - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - Pos = context->E[e].pos; - for (eRange=context->E[e].start; eRange < context->E[e].end; eRange++) - { - if (eRange > context->E[e].start) - visRep[(2*eRange) * (M+1) + Pos] = '|'; - visRep[(2*eRange+1) * (M+1) + Pos] = '|'; - } - } - - // Null terminate string and return it - visRep[(M+1) * 2*N] = '\0'; - return visRep; - } - - return NULL; -} - -/******************************************************************** - gp_DrawPlanar_RenderToFile() - Creates a rendition of the planar graph visibility representation - as a string, then dumps the string to the file. - ********************************************************************/ -int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName) -{ - if (sp_IsEmpty(theEmbedding->edgeHoles)) - { - FILE *outfile; - char *theRendition; - - if (strcmp(theFileName, "stdout") == 0) - outfile = stdout; - else if (strcmp(theFileName, "stderr") == 0) - outfile = stderr; - else outfile = fopen(theFileName, WRITETEXT); - - if (outfile == NULL) - return NOTOK; - - theRendition = _RenderToString(theEmbedding); - if (theRendition != NULL) - { - fprintf(outfile, "%s", theRendition); - free(theRendition); - } - - if (strcmp(theFileName, "stdout") == 0 || strcmp(theFileName, "stderr") == 0) - fflush(outfile); - - else if (fclose(outfile) != 0) - return NOTOK; - - return theRendition ? OK : NOTOK; - } - - return NOTOK; -} - -/******************************************************************** - _CheckVisibilityRepresentationIntegrity() - ********************************************************************/ - -int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context) -{ -graphP theEmbedding = context->theGraph; -int v, e, eTwin, EsizeOccupied, epos, eposIndex; - - if (sp_NonEmpty(context->theGraph->edgeHoles)) - return NOTOK; - - _ClearVisitedFlags(theEmbedding); - -/* Test whether the vertex values make sense and - whether the vertex positions are unique. */ - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - if (theEmbedding->M > 0) - { - if (context->VI[v].pos < 0 || - context->VI[v].pos >= theEmbedding->N || - context->VI[v].start < 0 || - context->VI[v].start > context->VI[v].end || - context->VI[v].end >= theEmbedding->M) - return NOTOK; - } - - // Has the vertex position been used by a vertex before vertex v? - if (gp_GetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding))) - return NOTOK; - - // Mark the vertex position as used by vertex v. - // Note that this marking is made on some other vertex unrelated to v - // We're just reusing the vertex visited array as cheap storage for a - // detector of reusing vertex position integers. - gp_SetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding)); - } - -/* Test whether the edge values make sense and - whether the edge positions are unique */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - /* Each edge has two index locations in the edge information array */ - eTwin = gp_GetTwinArc(theEmbedding, e); - - if (context->E[e].pos != context->E[eTwin].pos || - context->E[e].start != context->E[eTwin].start || - context->E[e].end != context->E[eTwin].end || - context->E[e].pos < 0 || - context->E[e].pos >= theEmbedding->M || - context->E[e].start < 0 || - context->E[e].start > context->E[e].end || - context->E[e].end >= theEmbedding->N) - return NOTOK; - - /* Get the recorded horizontal position of that edge, - a number between 0 and M-1 */ - - epos = context->E[e].pos; - - /* Convert that to an index in the graph structure so we - can use the visited flags in the graph's edges to - tell us whether the positions are being reused. */ - - eposIndex = (epos<<1) + gp_GetFirstEdge(theEmbedding); - eTwin = gp_GetTwinArc(theEmbedding, eposIndex); - - if (gp_GetEdgeVisited(theEmbedding, eposIndex) || gp_GetEdgeVisited(theEmbedding, eTwin)) - return NOTOK; - - gp_SetEdgeVisited(theEmbedding, eposIndex); - gp_SetEdgeVisited(theEmbedding, eTwin); - } - -/* Test whether any edge intersects any vertex position - for a vertex that is not an endpoint of the edge. */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - eTwin = gp_GetTwinArc(theEmbedding, e); - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - /* If the vertex is an endpoint of the edge, then... */ - - if (gp_GetNeighbor(theEmbedding, e) == v || gp_GetNeighbor(theEmbedding, eTwin) == v) - { - /* The vertical position of the vertex must be - at the top or bottom of the edge, */ - if (context->E[e].start != context->VI[v].pos && - context->E[e].end != context->VI[v].pos) - return NOTOK; - - /* The horizontal edge position must be in the range of the vertex */ - if (context->E[e].pos < context->VI[v].start || - context->E[e].pos > context->VI[v].end) - return NOTOK; - } - - /* If the vertex is not an endpoint of the edge... */ - - else // if (gp_GetNeighbor(theEmbedding, e) != v && gp_GetNeighbor(theEmbedding, eTwin) != v) - { - /* If the vertical position of the vertex is in the - vertical range of the edge ... */ - - if (context->E[e].start <= context->VI[v].pos && - context->E[e].end >= context->VI[v].pos) - { - /* And if the horizontal position of the edge is in the - horizontal range of the vertex, then return an error. */ - - if (context->VI[v].start <= context->E[e].pos && - context->VI[v].end >= context->E[e].pos) - return NOTOK; - } - } - } - } - - -/* All tests passed */ - - return OK; -} +/* +Copyright (c) 1997-2025, 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); +int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink); +char *_RenderToString(graphP theEmbedding); + +/******************************************************************** + _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 current 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; +} diff --git a/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.h b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.h new file mode 100644 index 0000000..f79ad9b --- /dev/null +++ b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.h @@ -0,0 +1,29 @@ +#ifndef GRAPH_DRAWPLANAR_H +#define GRAPH_DRAWPLANAR_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.private.h b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.private.h new file mode 100644 index 0000000..bdcd017 --- /dev/null +++ b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar.private.h @@ -0,0 +1,91 @@ +#ifndef GRAPH_DRAWPLANAR_PRIVATE_H +#define GRAPH_DRAWPLANAR_PRIVATE_H + +/* +Copyright (c) 1997-2025, 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 diff --git a/planarity_src/c/graphDrawPlanar_Extensions.c b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c similarity index 50% rename from planarity_src/c/graphDrawPlanar_Extensions.c rename to planarity_src/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c index 6fcc773..0a87e7a 100644 --- a/planarity_src/c/graphDrawPlanar_Extensions.c +++ b/planarity_src/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c @@ -1,722 +1,679 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 +/* +Copyright (c) 1997-2025, 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, char *extraData); +int _DrawPlanar_WritePostprocess(graphP theGraph, char **pExtraData); + +/* 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; -} + ****************************************************************************/ + +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) +{ +#ifdef USE_FASTER_1BASEDARRAYS + 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, char *extraData) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + if (context->functions.fpReadPostprocess(theGraph, extraData) != OK) + return NOTOK; + + else if (extraData != NULL && strlen(extraData) > 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 = 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, char **pExtraData) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + if (context->functions.fpWritePostprocess(theGraph, pExtraData) != OK) + return NOTOK; + else + { + int v, e, EsizeOccupied; + char line[64]; + int maxLineSize = 64, extraDataPos = 0; + char *extraData = (char *)calloc((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 = extraData; + } + + return OK; + } + + return NOTOK; +} diff --git a/planarity_src/c/graphLib/planarityRelated/graphEmbed.c b/planarity_src/c/graphLib/planarityRelated/graphEmbed.c new file mode 100644 index 0000000..b65787a --- /dev/null +++ b/planarity_src/c/graphLib/planarityRelated/graphEmbed.c @@ -0,0 +1,1376 @@ +/* +Copyright (c) 1997-2025, 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 _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); + +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 _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); +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; +} diff --git a/planarity_src/c/graphLib/planarityRelated/graphIsolator.c b/planarity_src/c/graphLib/planarityRelated/graphIsolator.c new file mode 100644 index 0000000..4c53eff --- /dev/null +++ b/planarity_src/c/graphLib/planarityRelated/graphIsolator.c @@ -0,0 +1,834 @@ +/* +Copyright (c) 1997-2025, 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 _MarkDFSPath(graphP theGraph, int ancestor, int descendant); + +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; +} diff --git a/planarity_src/c/graphLib/planarityRelated/graphNonplanar.c b/planarity_src/c/graphLib/planarityRelated/graphNonplanar.c new file mode 100644 index 0000000..8d7ca7d --- /dev/null +++ b/planarity_src/c/graphLib/planarityRelated/graphNonplanar.c @@ -0,0 +1,829 @@ +/* +Copyright (c) 1997-2025, 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 _MarkLowestXYPath(graphP theGraph); +int _MarkClosestXYPath(graphP theGraph, int targetVertex); +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) != OK || theGraph->IC.py == NIL) + 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() + + Sets the visited flags on the highest X-Y path, i.e. the one closest to the + root vertex R of the biconnected component containing X and Y as well as a + pertinent vertex W that the Walkdown could not reach due to the future + pertinent vertices X and Y being along both external face paths + emanating from the root R. + + The caller receives an OK result if the method succeeded in operating or + NOTOK on an internal failure. However, there may or may not be any X-Y path, + in which case no visitation flags are set and the return result is still OK + because the caller must decide whether the absence of an X-Y path is an + operational error. + + This method also sets the isolator context's points of attachment on the + external face of the marked X-Y path, if there was an X-Y path. So, the + caller can also use this call to decide if there was an X-Y path by + testing whether theGraph->IC.px and py have been set to non-NIL values. + ****************************************************************************/ + +int _MarkHighestXYPath(graphP theGraph) +{ + return _MarkClosestXYPath(theGraph, theGraph->IC.r); +} + +/**************************************************************************** + _MarkLowestXYPath() + + Sets the visited flags on the lowest X-Y path, i.e. the one closest to the + pertinent vertex W that the Walkdown could not reach due to future pertinent + vertices X and Y along both external face paths emanating from the root R + of the biconnected component containing W, X and Y. + + The caller receives an OK result if the method succeeded in operating or + NOTOK on an internal failure. However, there may or may not be any X-Y path, + in which case no visitation flags are set and the return result is still OK + because the caller must decide whether the absence of an X-Y path is an + operational error. + + This method also sets the isolator context's points of attachment on the + external face of the marked X-Y path, if there was an X-Y path. So, the + caller can also use this call to decide if there was an X-Y path by + testing whether theGraph->IC.px and py have been set to non-NIL values. + ****************************************************************************/ + +int _MarkLowestXYPath(graphP theGraph) +{ + return _MarkClosestXYPath(theGraph, theGraph->IC.w); +} + +/**************************************************************************** + _MarkClosestXYPath() + + This method searches for and marks the X-Y path in the bicomp rooted by R + that is either closest to R (highest) or closest to W (lowest). This method + will return NOTOK if the targetVertex parameter is other than R or W. + + The closest X-Y path is the path through the inside of the bicomp that + only attaches to the external face at its two endpoint vertices and + those endpoints are closer along the external face path emanating from + the targetVertex than the attachment points of any other X-Y path. + + This method returns NOTOK if there is an internal processing error and + otherwise it returns OK. If there is no X-Y path, the method still returns + OK to indicate no internal failures, but on return the caller can detect + whether there was an X-Y path by testing whether the attachment points in + the isolator context have been set to non-NIL values. Specifically, test + whether theGraph->IC.px and py have been set to non-NIL values. The caller + must decide whether the absence of an X-Y path is an error. For example, + in core planarity, the proof of correctness guarantees an X-Y path exists + by the time this method is called, so that caller would decide to return + NOTOK since there should be an X-Y path. + + If there is an X-Y path, it is marked using the visited flags on the + vertices and edges. + + PRECONDITION: 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 of vertices and edges. + First, we remove all edges incident to the targetVertex, except the two edges + that join it to the external face. The result is that the targetVertex 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 closest 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 + the targetVertex, but the edge to the targetVertex has been temporarily hidden. + The algorithm removes loops of vertices and edges along the proper face so + that only the desired path is identified. + + To walk the proper face containing the targetVertex, we first identify an + arc that will be considered to be the one used to enter the targetVertex. + When the first loop iteration exits the targetVertex, it comes out on the + RXW side (even though it may be an internal vertex not marked RXW). + Then we take either the next arc (if targetVertex==W) or predecessor arc + (if targetVertex==R) at every subsequent corner to determine the exit arc + for the vertex. Then, we use the twin arc of the exit arc to determine the + entry arc for the next vertex. + + For each vertex, we mark as visited the vertex as well as both arcs of + the edge used to enter the vertex. We also push the visited vertices and + edges onto a stack. Each time the traversal lands on an external face + vertex on the RXW side, it is recorded as a candidate point of attachment Px. + We also pop and unmark all previously visited vertices and edges because they + are now known to not be part of the internal X-Y path after encountering the + point of attachment. Instead, these preceding edges and vertices were part + of a path parallel to the external face that does not obstruct the space + between R and W inside the bicomp. + + As we walk the proper face, we keep track of the last vertex P_x we visited of + type RXW (high or low). If we encounter antipodalVertex of the targetVertex + (i.e., W if the targetVertex is R, or R if the targetVertex is W), then there + is no obstructing X-Y path since we removed only edges incident to the + targetVertex, so we pop the stack unmarking everything then clear the + X-Y path points of attachment in the isolator context so the caller will know + that no X-Y path was found (and then we return OK since there was no internal + error). + + If, during the traversal, we encounter a vertex Z previously visited, then we + pop the stack, unmarking the vertices and edges popped, until we find the + prior occurrence of Z on the stack. This is because we have traversed a path + that would connect back to the targetVertex had we not hidden the internal + edges of the targetVertex. + + Otherwise, the first time we encounter a vertex of type 'RYW', we stop + because the obstructing X-Y path has been marked visited and its points of + attachment to the external face have been found. This second point of + attachment Py is stored in the isolator context. + + Once the X-Y path is identified (or once we have found that there is no + X-Y path), we restore the previously hidden edges incident to the targetVertex. + + 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 hiding + the edges incident to the targetVertex. 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 OK on successful operation and NOTOK on internal failure. + Also, if an X-Y path is found (which will be the closest), then + the graph isolator context contains its attachment points on the + external face of the bicomp rooted by R, and the edges and vertices + in the X-Y path have been marked visited. + ****************************************************************************/ + +int _MarkClosestXYPath(graphP theGraph, int targetVertex) +{ + int e, Z; + int R, W, antipodalVertex; + int stackBottom1, stackBottom2; + + /* Initialization */ + + R = theGraph->IC.r; + W = theGraph->IC.w; + theGraph->IC.px = theGraph->IC.py = NIL; + + /* This method only makes sense for a targetVertex of R or W */ + if (targetVertex != R && targetVertex != W) + return NOTOK; + + /* The vertex opposite the targetVertex is needed to detect when + there is no X-Y path */ + + antipodalVertex = targetVertex == R ? W : R; + + /* 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 targetVertex (R or W) */ + + if (_HideInternalEdges(theGraph, targetVertex) != 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 targetVertex to find and mark the + closest X-Y path. */ + + Z = targetVertex; + + // Now we will get the arc that we consider to be the arc used to enter + // the targetVertex (which will be an edge on the RYW side, and the + // first line of the loop code will get the previous or next arc to exit + // the targetVertex on the RXW side of the bicomp) + e = targetVertex == R ? gp_GetLastArc(theGraph, R) : gp_GetFirstArc(theGraph, W); + + 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 the targetVertex */ + + // Get the opposing arc of the corner at vertex Z, as the arc to exit Z + e = targetVertex == R ? gp_GetPrevArcCircular(theGraph, e) + : gp_GetNextArcCircular(theGraph, e); + + // Now use the exit arc to get the next Z to visit + Z = gp_GetNeighbor(theGraph, e); + + // And get the entry arc of the new Z being visited + 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 the antipodalVertex of the targetVertex (W if R, R if 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 == antipodalVertex) + { + 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 closest 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 */ + + if (!gp_IsVertex(theGraph->IC.py)) + theGraph->IC.px = NIL; + + return OK; +} + +/**************************************************************************** + _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 occurred, 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; +} diff --git a/planarity_src/c/graphLib/planarityRelated/graphOuterplanarObstruction.c b/planarity_src/c/graphLib/planarityRelated/graphOuterplanarObstruction.c new file mode 100644 index 0000000..0f4b4e6 --- /dev/null +++ b/planarity_src/c/graphLib/planarityRelated/graphOuterplanarObstruction.c @@ -0,0 +1,196 @@ +/* +Copyright (c) 1997-2025, 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) != OK || theGraph->IC.py == NIL) + 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; +} diff --git a/planarity_src/c/graphTests.c b/planarity_src/c/graphLib/planarityRelated/graphTests.c similarity index 55% rename from planarity_src/c/graphTests.c rename to planarity_src/c/graphLib/planarityRelated/graphTests.c index 31a48a3..efb4dc8 100644 --- a/planarity_src/c/graphTests.c +++ b/planarity_src/c/graphLib/planarityRelated/graphTests.c @@ -1,1008 +1,975 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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; -} +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define GRAPHTEST_C + +#include "../graph.h" +#include "../lowLevelUtils/stack.h" + +extern void _ClearVertexVisitedFlags(graphP theGraph, int); + +/* Private function declarations (some exported to system) */ + +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 _getImageVertices(graphP theGraph, int *degrees, int maxDegree, + int *imageVerts, int maxNumImageVerts); +int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, + int *degrees, int *imageVerts); +int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts); +int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts); + +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; +} diff --git a/planarity_src/c/graphNonplanar.c b/planarity_src/c/graphNonplanar.c deleted file mode 100644 index 134546b..0000000 --- a/planarity_src/c/graphNonplanar.c +++ /dev/null @@ -1,770 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#define GRAPHNONPLANAR_C - -#include "graph.h" - -/* Imported functions */ - -extern void _InitIsolatorContext(graphP theGraph); -extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); -extern int _HideInternalEdges(graphP theGraph, int vertex); -extern int _RestoreInternalEdges(graphP theGraph, int stackBottom); - -//extern int _OrientVerticesInEmbedding(graphP theGraph); -extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); - -/* Private functions (exported to system) */ - -int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); -int _InitializeNonplanarityContext(graphP theGraph, int v, int R); - -int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY); -int _FindPertinentVertex(graphP theGraph); -int _SetVertexTypesForMarkingXYPath(graphP theGraph); - -int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom); - -int _MarkHighestXYPath(graphP theGraph); -int _MarkZtoRPath(graphP theGraph); -int _FindFuturePertinenceBelowXYPath(graphP theGraph); - -/**************************************************************************** - _ChooseTypeOfNonplanarityMinor() - ****************************************************************************/ - -int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R) -{ -int X, Y, W, Px, Py, Z; - -/* Create the initial non-planarity minor state in the isolator context */ - - if (_InitializeNonplanarityContext(theGraph, v, R) != OK) - return NOTOK; - - R = theGraph->IC.r; - X = theGraph->IC.x; - Y = theGraph->IC.y; - W = theGraph->IC.w; - -/* If the root copy is not a root copy of the current vertex v, - then the Walkdown terminated because it couldn't find - a viable path along a child bicomp, which is Minor A. */ - - if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) - { - theGraph->IC.minorType |= MINORTYPE_A; - return OK; - } - -/* If W has a pertinent and future pertinent child bicomp, then we've found Minor B */ - - if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) - { - if (gp_GetVertexLowpoint(theGraph, gp_GetVertexLastPertinentRootChild(theGraph, W)) < v) - { - theGraph->IC.minorType |= MINORTYPE_B; - return OK; - } - } - -/* Find the highest obstructing X-Y path */ - - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - Px = theGraph->IC.px; - Py = theGraph->IC.py; - -/* If either point of attachment is 'high' (P_x closer to R than X - or P_y closer to R than Y along external face), then we've - matched Minor C. */ - - if (gp_GetVertexObstructionType(theGraph, Px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW || - gp_GetVertexObstructionType(theGraph, Py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW) - { - theGraph->IC.minorType |= MINORTYPE_C; - return OK; - } - -/* For Minor D, we search for a path from an internal - vertex Z along the X-Y path up to the root R of the bicomp. */ - - if (_MarkZtoRPath(theGraph) != OK) - return NOTOK; - - if (gp_IsVertex(theGraph->IC.z)) - { - theGraph->IC.minorType |= MINORTYPE_D; - return OK; - } - -/* For Minor E, we search for an future pertinent vertex Z - below the points of attachment of the X-Y path */ - - Z = _FindFuturePertinenceBelowXYPath(theGraph); - if (gp_IsVertex(Z)) - { - theGraph->IC.z = Z; - theGraph->IC.minorType |= MINORTYPE_E; - return OK; - } - - return NOTOK; -} - -/**************************************************************************** - _InitializeNonplanarityContext() - - This method finds the stopping vertices X and Y, and the pertinent vertex W - of a bicomp rooted by vertex R. - - If R is NIL, the routine first determines which bicomp produced non-planarity - condition. If the stack is non-empty, then R is on the top of the stack. - Otherwise, an unembedded fwdArc from the fwdArcList of vertex v is used in - combination with the sortedDFSChildList of v to determine R. - - If the parameter R was not NIL, then this method assumes it must operate - only on the bicomp rooted by R, and it also assumes that the caller has - not cleared the visited flags in the bicomp, so they are cleared. - - This routine imparts consistent orientation to all vertices in bicomp R - since several subroutines count on this. The edge signs are preserved so that - the original orientations of all vertices can be restored. If the vertices - of the embedding are already consistently oriented, then this operation - simply has no effect. - - Finally, in the bicomp R, the vertex types of all non-root vertices on the - external face are classified according to whether or not they are closer to - the root R than X and Y along the external face paths (R X W) and (R Y W). - ****************************************************************************/ - -int _InitializeNonplanarityContext(graphP theGraph, int v, int R) -{ - // Blank out the isolator context, then assign the input graph reference - // and the current vertext v into the context. - _InitIsolatorContext(theGraph); - theGraph->IC.v = v; - - // The bicomp root provided was the one on which the WalkDown was performed, - // but in the case of Minor A, the central bicomp of the minor is at the top - // of the stack, so R must be changed to that value. - if (sp_NonEmpty(theGraph->theStack)) - { - int dummy; - sp_Pop2(theGraph->theStack, R, dummy); - } - - theGraph->IC.r = R; - - // A number of subroutines require the main bicomp of the minor to be - // consistently oriented and its visited flags clear. - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - - // Now we find the active vertices along both external face paths - // extending from R. - _FindActiveVertices(theGraph, R, &theGraph->IC.x, &theGraph->IC.y); - - // Now, we obtain the pertinent vertex W on the lower external face - // path between X and Y (that path that does not include R). - theGraph->IC.w = _FindPertinentVertex(theGraph); - - // Now we can classify the vertices along the external face of the bicomp - // rooted at R as 'high RXW', 'low RXW', 'high RXY', 'low RXY' - if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) - return NOTOK; - - // All work is done, so return success - return OK; -} - -/******************************************************************** - _GetNeighborOnExtFace() - - Each vertex contains two 'link' index pointers that indicate the - first and last adjacency list arc. If the vertex is on the external face, - then these two arcs are also on the external face. We want to take one of - those edges to get to the next vertex on the external face. - On input *pPrevLink indicates which link we followed to arrive at - curVertex. On output *pPrevLink will be set to the link we follow to - get into the next vertex. - To get to the next vertex, we use the opposite link from the one used - to get into curVertex. This takes us to an edge node. The twinArc - of that edge node, carries us to an edge node in the next vertex. - At least one of the two links in that edge node will lead to a vertex - node in G, which is the next vertex. Once we arrive at the next - vertex, at least one of its links will lead back to the edge node, and - that link becomes the output value of *pPrevLink. - - NOTE: This method intentionally ignores the extFace optimization - links. It is invoked when the "real" external face must be - traversed and hence when the constant time guarantee is not - needed from the extFace short-circuit that connects the - bicomp root to the first active vertices along each external - face path emanating from the bicomp root. - ********************************************************************/ - -int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink) -{ - /* Exit curVertex from whichever link was not previously used to enter it */ - - int arc = gp_GetArc(theGraph, curVertex, 1^(*pPrevLink)); - int nextVertex = gp_GetNeighbor(theGraph, arc); - - /* This if stmt assigns the new prev link that tells us which edge - record was used to enter nextVertex (so that we exit from the - opposing edge record). - - However, if we are in a singleton bicomp, then both links in nextVertex - lead back to curVertex. We want the two arcs of a singleton bicomp to - act like a cycle, so we just don't change the prev link in this case. - - But when nextVertex has more than one edge, we need to figure out - whether the first edge or last edge (which are the two on the external - face) was used to enter nextVertex so we can exit from the other one - as traversal of the external face continues later. */ - - if (gp_GetFirstArc(theGraph, nextVertex) != gp_GetLastArc(theGraph, nextVertex)) - *pPrevLink = gp_GetTwinArc(theGraph, arc) == gp_GetFirstArc(theGraph, nextVertex) ? 0 : 1; - - return nextVertex; -} - -/**************************************************************************** - _FindActiveVertices() - - Descends from the root of a bicomp R along both external face paths (which - are indicated by the first and last arcs in R's adjacency list), returning - the first active vertex appearing in each direction. - ****************************************************************************/ - -void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY) -{ -int XPrevLink=1, YPrevLink=0, v=theGraph->IC.v; - - *pX = _GetNeighborOnExtFace(theGraph, R, &XPrevLink); - *pY = _GetNeighborOnExtFace(theGraph, R, &YPrevLink); - - // For planarity algorithms, advance past inactive vertices - // For outerplanarity algorithms, ignore the notion of inactive vertices - // since all vertices must remain on the external face. - if (!(theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR)) - { - gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v); - while (INACTIVE(theGraph, *pX, v)) - { - *pX = _GetNeighborOnExtFace(theGraph, *pX, &XPrevLink); - gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v); - } - - gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v); - while (INACTIVE(theGraph, *pY, v)) - { - *pY = _GetNeighborOnExtFace(theGraph, *pY, &YPrevLink); - gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v); - } - } -} - -/**************************************************************************** - _FindPertinentVertex() - - Get the first vertex after x. Since x was obtained using a prevlink of 1 on r, - we use the same prevlink so we don't go back to R. - Then, we proceed around the lower path until we find a vertex W that either - has pertinent child bicomps or is directly adjacent to the current vertex v. - ****************************************************************************/ - -int _FindPertinentVertex(graphP theGraph) -{ -int W=theGraph->IC.x, WPrevLink=1; - - W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); - - while (W != theGraph->IC.y) - { - if (PERTINENT(theGraph, W)) - return W; - - W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); - } - - return NIL; -} - -/**************************************************************************** - _SetVertexTypesForMarkingXYPath() - - Label the vertices along the external face of the bicomp rooted at R as - 'high RXW', 'low RXW', 'high RXY', 'low RXY' - ****************************************************************************/ - -int _SetVertexTypesForMarkingXYPath(graphP theGraph) -{ - int v, R, X, Y, W, Z, ZPrevLink, ZType; - - // Unpack the context for efficiency of loops - v = theGraph->IC.v; - R = theGraph->IC.r; - X = theGraph->IC.x; - Y = theGraph->IC.y; - W = theGraph->IC.w; - - // Ensure basic preconditions of this routine are met - if (gp_IsNotVertex(R) || gp_IsNotVertex(X) || gp_IsNotVertex(Y) || gp_IsNotVertex(W)) - return NOTOK; - - // Clear the type member of each vertex in the bicomp - if (_ClearVertexTypeInBicomp(theGraph, R) != OK) - return NOTOK; - - // Traverse from R to W in the X direction - ZPrevLink = 1; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RXW; - while (Z != W) - { - if (Z == X) ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RXW; - gp_ResetVertexObstructionType(theGraph, Z, ZType); - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - // Traverse from R to W in the Y direction - ZPrevLink = 0; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RYW; - while (Z != W) - { - if (Z == Y) ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RYW; - gp_ResetVertexObstructionType(theGraph, Z, ZType); - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - return OK; -} - -/**************************************************************************** - _PopAndUnmarkVerticesAndEdges() - - Pop all vertex/edge pairs from the top of the stack up to a terminating - vertex Z and mark as unvisited. If Z is NIL, then all vertex/edge pairs - are popped and marked as unvisited. - The stackBottom indicates where other material besides the vertex/edge - pairs may appear. - ****************************************************************************/ - -int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom) -{ -int V, e; - - // Pop vertex/edge pairs until all have been popped from the stack, - // and all that's left is what was under the pairs, or until... - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - // If we pop the terminating vertex Z, then put it back and break - if (V == Z) - { - sp_Push(theGraph->theStack, V); - break; - } - - // Otherwise, pop the edge part of the vertex/edge pair - sp_Pop(theGraph->theStack, e); - - // Now unmark the vertex and edge (i.e. revert to "unvisited") - gp_ClearVertexVisited(theGraph, V); - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - return OK; -} - -/**************************************************************************** - _MarkHighestXYPath() - - An X-Y path in the bicomp rooted by R is a path attached to the external - face at points Px and Py that separates W from R such that a back edge (R, W) - cannot be embedded within the bicomp. Recall that R is a root copy of v, so - (R, W) is the representative of (v, W). Also, note that W is pertinent if - either W *or* one of its descendants in a separate bicomp has, in the input - graph, a back edge to v. - - If no X-Y path separating W from R is found, then NOTOK is returned because - the proof of correctness guarantees that one exists (although this routine - can also be used to help test for the existence of an X-Y path, and NOTOK - means 'no' in that case). - - The desired output is to set the 'visited' flags of the X-Y path with - highest points of attachment to the external face (i.e. the points of - attachment that are closest to R along the external face). This includes - marking both the vertices and edges along the X-Y path. - - Previously, during non-planarity context initialization, the vertices along - the external face (other than R and W) have been classified as 'high RXW', - 'low RXW', 'high RXY', or 'low RXY'. Once the vertices have been categorized, - we proceed with trying to set the visitation flags in the way described above. - First, we remove all edges incident to R except the two edges that join R to - the external face. The result is that R and its two remaining edges are a - 'corner' in the external face but also in a single proper face whose boundary - includes the X-Y path with the highest attachment points. Thus, we simply need - to walk this proper face to find the desired X-Y path. Note, however, that the - resulting face boundary may have attached cut vertices. Any such separable - component contains a vertex neighbor of R, but the edge to R has been - temporarily removed. The algorithm removes loop of vertices and edges along - the proper face so that only a path is identified. - - To walk the proper face containing R, we begin with its first arc successor, - then take the *predecessor* arc at every subsequent corner. For each vertex, - we mark as visited the vertex as well as the edge used to enter the vertex - (except for the edge used to enter the RXW vertex). We also push the visited - vertices and edges onto a stack. - - As we walk the proper face, we keep track of the last vertex P_x we visited of - type RXW (high or low). Each time we encounter a vertex of type RXW (high or - low), we pop the stack and unmark all of the edges and vertices visited because - they were part of a path parallel to the external face that does not obstruct - W from reaching R within the bicomp. If we encounter vertex W, then there is - no obstructing X-Y path since we removed only edges incident to R, so we pop - the stack unmarking everything then return NOTOK as stated above. If we - encounter a vertex Z previously visited, then we pop the stack, unmarking the - vertices and edges popped, until we find the prior occurence of Z on the stack. - - Otherwise, the first time we encounter a vertex P_y of type 'RYW', we stop - because the obstructing X-Y path has been marked visited and its points of - connection P_x and P_y have been found. - - Once the X-Y path is identified, we restore the edges incident to R. - - This method uses the stack, but it preserves any prior content. - The stack space used is no greater than 3N. The first N accounts for removing - the edges incident to R. The other 2N accounts for the fact that each - iteration of the main loop visits a vertex, pushing its index and the - location of an edge record. If a vertex is encountered that is already - on the stack, then it is not pushed again (and in fact part of the stack - is removed). - - Returns TRUE if the X-Y path is found, FALSE otherwise. - In debug mode it can also return NOTOK. This is equivalent to FALSE, but - NOTOK is better for documenting the error condition in the code, and - it produces a debug message. Also, in many cases the equivalent-to-FALSE - result is an error condition for the caller, so NOTOK usually percolates up. - ****************************************************************************/ - -int _MarkHighestXYPath(graphP theGraph) -{ -int e, Z; -int R, X, Y, W; -int stackBottom1, stackBottom2; - -/* Initialization */ - - R = theGraph->IC.r; - X = theGraph->IC.x; - Y = theGraph->IC.y; - W = theGraph->IC.w; - theGraph->IC.px = theGraph->IC.py = NIL; - -/* Save the stack bottom before we start hiding internal edges, so - we will know how many edges to restore */ - - stackBottom1 = sp_GetCurrentSize(theGraph->theStack); - -/* Remove the internal edges incident to vertex R */ - - if (_HideInternalEdges(theGraph, R) != OK) - return NOTOK; - -/* Now we're going to use the stack to collect the vertices of potential - * X-Y paths, so we need to store where the hidden internal edges are - * located because we must, at times, pop the collected vertices if - * the path being collected doesn't work out. */ - - stackBottom2 = sp_GetCurrentSize(theGraph->theStack); - -/* Walk the proper face containing R to find and mark the highest - X-Y path. Note that if W is encountered, then there is no - intervening X-Y path, so we would return FALSE in that case. */ - - Z = R; - // This setting of e is the arc equivalent of prevLink=1 - // As loop progresses, e indicates the arc used to enter Z, not the exit arc - e = gp_GetLastArc(theGraph, R); - - while (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_HIGH_RYW && - gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - { - /* Advance e and Z along the proper face containing R */ - - e = gp_GetPrevArcCircular(theGraph, e); - Z = gp_GetNeighbor(theGraph, e); - e = gp_GetTwinArc(theGraph, e); - - /* If Z is already visited, then pop everything since the last time - we visited Z because its all part of a separable component. */ - - if (gp_GetVertexVisited(theGraph, Z)) - { - if (_PopAndUnmarkVerticesAndEdges(theGraph, Z, stackBottom2) != OK) - return NOTOK; - } - - /* If we have not visited this vertex before... */ - - else - { - /* If we find W, then there is no X-Y path. Never happens - for Kuratowski subgraph isolator, but this routine is - also used to test for certain X-Y paths. - So, we clean up and bail out in that case. */ - - if (Z == W) - { - if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK) - return NOTOK; - break; - } - - /* If we found another vertex along the RXW path, then blow off - all the vertices we visited so far because they're not part of - the obstructing path */ - - if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW || - gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - { - theGraph->IC.px = Z; - if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK) - return NOTOK; - } - - /* Push the current vertex onto the stack of vertices visited - since the last RXW vertex was encountered */ - - sp_Push(theGraph->theStack, e); - sp_Push(theGraph->theStack, Z); - - /* Mark the vertex Z as visited as well as its edge of entry - (except the entry edge for P_x).*/ - - gp_SetVertexVisited(theGraph, Z); - if (Z != theGraph->IC.px) - { - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - /* If we found an RYW vertex, then we have successfully finished - identifying the highest X-Y path, so we record the point of - attachment and break the loop. */ - - if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW || - gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - { - theGraph->IC.py = Z; - break; - } - } - } - -/* Remove any remaining vertex-edge pairs on the top of the stack, then - Restore the internal edges incident to R that were previously removed. */ - - sp_SetCurrentSize(theGraph->theStack, stackBottom2); - - if (_RestoreInternalEdges(theGraph, stackBottom1) != OK) - return NOTOK; - -/* Return the result */ - - return gp_IsVertex(theGraph->IC.py) ? TRUE : FALSE; -} - -/**************************************************************************** - _MarkZtoRPath() - - This function assumes that _MarkHighestXYPath() has already been called, - which marked as visited the vertices and edges along the X-Y path. - - We begin at the point of attachment P_x, take the last arc and traverse - the predecessor arcs until we find one marked visited, which leads to the - first internal vertex along the X-Y path. We begin with this vertex - (and its edge of entry), and we run until we find P_y. For each internal - vertex Z and its edge of entry ZPrevArc, we take the predecessor edge record - of ZPrevArc. This is called ZNextArc. If ZNextArc is marked visited - then it is along the X-Y path, so we use it to exit Z and go to the next - vertex on the X-Y path. - - If ZNextArc is not visited, then when _MarkHighestXYPath() ran, it exited - Z from ZNextArc, then eventually reentered Z. In other words, Z became a - cut vertex when we removed the internal edges incident to R. Thus, ZNextArc - indicates the first edge in an internal path to R. - - When we find an unvisited ZNextArc, we stop running the X-Y path and instead - begin marking the Z to R path. We move to successive vertices using a - twin arc then its predecessor arc in the adjacency list, only this time - we have not removed the internal edges incident to R, so this technique does - eventually lead us all the way to R. - - If we do not find an unvisited ZNextArc for any vertex Z on the X-Y path and - inside the bicomp, then there is no Z to R path, so we return. - ****************************************************************************/ - -int _MarkZtoRPath(graphP theGraph) -{ -int ZPrevArc, ZNextArc, Z, R, Px, Py; - -/* Initialize */ - - R = theGraph->IC.r; - Px = theGraph->IC.px; - Py = theGraph->IC.py; - theGraph->IC.z = NIL; - -/* Begin at Px and search its adjacency list for the edge leading to - the first internal vertex of the X-Y path. */ - - Z = Px; - ZNextArc = gp_GetLastArc(theGraph, Z); - while (ZNextArc != gp_GetFirstArc(theGraph, Z)) - { - if (gp_GetEdgeVisited(theGraph, ZNextArc)) break; - - ZNextArc = gp_GetPrevArc(theGraph, ZNextArc); - } - - if (!gp_GetEdgeVisited(theGraph, ZNextArc)) - return NOTOK; - -/* For each internal vertex Z, determine whether it has a path to root. */ - - while (gp_GetEdgeVisited(theGraph, ZNextArc)) - { - ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); - ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc); - } - - ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); - Z = gp_GetNeighbor(theGraph, ZPrevArc); - -/* If there is no Z to R path, return */ - - if (Z == Py) return OK; - -/* Otherwise, store Z in the isolation context */ - - theGraph->IC.z = Z; - -/* Walk the proper face starting with (Z, ZNextArc) until we reach R, marking - the vertices and edges encountered along the way, then Return OK. */ - - while (Z != R) - { - /* If we ever encounter a non-internal vertex (other than the root R), - then corruption has occured, so we return NOTOK */ - - if (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_UNKNOWN) - return NOTOK; - - /* Go to the next vertex indicated by ZNextArc */ - - Z = gp_GetNeighbor(theGraph, ZNextArc); - - /* Mark the next vertex and the edge leading to it as visited. */ - - gp_SetEdgeVisited(theGraph, ZNextArc); - gp_SetEdgeVisited(theGraph, ZPrevArc); - gp_SetVertexVisited(theGraph, Z); - - /* Go to the next edge in the proper face */ - - ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc); - ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); - } - -/* Found Z to R path, so indicate as much to caller */ - - return OK; -} - -/**************************************************************************** - _FindFuturePertinenceBelowXYPath() - - Get a future pertinent vertex along the lower external face path between - the points of attachment P_x and P_y of a 'low' X-Y Path. - NOTE: By the time this function is called, Px and Py have already been found - to be at or below X and Y. - ****************************************************************************/ - -int _FindFuturePertinenceBelowXYPath(graphP theGraph) -{ -int Z=theGraph->IC.px, ZPrevLink=1, - Py=theGraph->IC.py, v=theGraph->IC.v; - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - - while (Z != Py) - { - gp_UpdateVertexFuturePertinentChild(theGraph, Z, v); - if (FUTUREPERTINENT(theGraph, Z, v)) - return Z; - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - return NIL; -} diff --git a/planarity_src/c/graphOuterplanarObstruction.c b/planarity_src/c/graphOuterplanarObstruction.c deleted file mode 100644 index 661a779..0000000 --- a/planarity_src/c/graphOuterplanarObstruction.c +++ /dev/null @@ -1,241 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "graph.h" - -/* Imported functions */ - -extern void _ClearVisitedFlags(graphP); - -extern int _JoinBicomps(graphP theGraph); - -extern int _InitializeNonplanarityContext(graphP theGraph, int v, int R); -extern int _MarkHighestXYPath(graphP theGraph); - -//extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); -extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); -extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); - -extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); - -extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); - -extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -/* Private function declarations (exported to system) */ - -int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R); - -int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); - -int _IsolateOuterplanarityObstructionA(graphP theGraph); -int _IsolateOuterplanarityObstructionB(graphP theGraph); -int _IsolateOuterplanarityObstructionE(graphP theGraph); - -/**************************************************************************** - _ChooseTypeOfNonOuterplanarityMinor() - A constant time implementation is easily feasible but only constant amortized - time is needed for the outerplanarity obstruction isolation, which also - benefits from having the bicomp rooted by R oriented. - If an extension algorithm requires constant actual time, then this function - should not be used and instead the minor should be decided without orienting - the bicomp. - ****************************************************************************/ - -int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R) -{ -int X, Y, W; - - // Create the initial non-outerplanarity obstruction isolator state. - if (_InitializeNonplanarityContext(theGraph, v, R) != OK) - return NOTOK; - - R = theGraph->IC.r; - X = theGraph->IC.x; - Y = theGraph->IC.y; - W = theGraph->IC.w; - - // If the root copy is not a root copy of the current vertex v, - // then the Walkdown terminated on a descendant bicomp, which is Minor A. - if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) - { - theGraph->IC.minorType |= MINORTYPE_A; - return OK; - } - - // If W has a pertinent child bicomp, then we've found Minor B. - // Notice this is different from planarity, in which minor B is indicated - // only if the pertinent child bicomp is also future pertinent. - if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) - { - theGraph->IC.minorType |= MINORTYPE_B; - return OK; - } - - // The only other result is minor E (we will search for the X-Y path later) - theGraph->IC.minorType |= MINORTYPE_E; - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarObstruction() - ****************************************************************************/ - -int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R) -{ -int RetVal; - -/* A subgraph homeomorphic to K_{2,3} or K_4 will be isolated by using the visited - flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we - subsequently set visited on all edges and vertices in the homeomorph. */ - - _ClearVisitedFlags(theGraph); - -/* Next we determineg which of the non-outerplanarity Minors was encountered - and the principal bicomp on which the isolator will focus attention. */ - - if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - -/* Find the path connecting the pertinent vertex w with the current vertex v */ - - if (theGraph->IC.minorType & MINORTYPE_B) - { - isolatorContextP IC = &theGraph->IC; - int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); - - if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE) - return NOTOK; - } - else - { - isolatorContextP IC = &theGraph->IC; - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - } - -/* For minor E, we need to find and mark an X-Y path */ - - if (theGraph->IC.minorType & MINORTYPE_E) - { - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - } - -/* Call the appropriate isolator */ - - if (theGraph->IC.minorType & MINORTYPE_A) - RetVal = _IsolateOuterplanarityObstructionA(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_B) - RetVal = _IsolateOuterplanarityObstructionB(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_E) - RetVal = _IsolateOuterplanarityObstructionE(theGraph); - else - RetVal = NOTOK; - -/* Delete the unmarked edges and vertices, and return */ - - if (RetVal == OK) - RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph); - - return RetVal; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionA(): Isolate a K2,3 homeomorph - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionA(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->v, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionB(): Isolate a K2,3 homeomorph - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionB(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionE(): Isolate a K4 homeomorph - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionE(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} diff --git a/planarity_src/c/graphStructures.h b/planarity_src/c/graphStructures.h deleted file mode 100644 index cec9cdf..0000000 --- a/planarity_src/c/graphStructures.h +++ /dev/null @@ -1,837 +0,0 @@ -#ifndef GRAPHSTRUCTURE_H -#define GRAPHSTRUCTURE_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include -#include "appconst.h" -#include "listcoll.h" -#include "stack.h" - -#include "graphFunctionTable.h" -#include "graphExtensions.private.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// A return value to indicate success prior to completely processing a graph, whereas -// OK signifies EMBEDDABLE (no unreducible obstructions) and NOTOK signifies an exception. -#define NONEMBEDDABLE -1 - -// The initial setting for the edge storage capacity expressed as a constant factor of N, -// which is the number of vertices in the graph. By default, array E is allocated enough -// space to contain 3N edges, which is 6N arcs (half edges), but this initial setting -// can be overridden using gp_EnsureArcCapacity(), which is especially efficient if done -// before calling gp_InitGraph() or gp_Read(). -#define DEFAULT_EDGE_LIMIT 3 - -/******************************************************************** - Edge Record Definition - - An edge is defined by a pair of edge records, or arcs, allocated in - array E of a graph. An edge record represents the edge in the - adjacency list of each vertex to which the edge is incident. - - link[2]: the next and previous edge records (arcs) in the adjacency - list that contains this edge record. - - v: The vertex neighbor of the vertex whose adjacency list contains - this edge record (an index into array V). - - flags: Bits 0-15 reserved for library; bits 16 and higher for apps - Bit 0: Visited - Bit 1: DFS type has been set, versus not set - Bit 2: DFS tree edge, versus cycle edge (co-tree edge, etc.) - Bit 3: DFS arc to descendant, versus arc to ancestor - Bit 4: Inverted (same as marking an edge with a "sign" of -1) - Bit 5: Arc is directed into the containing vertex only - Bit 6: Arc is directed from the containing vertex only - ********************************************************************/ - -typedef struct -{ - int link[2]; - int neighbor; - unsigned flags; -} edgeRec; - -typedef edgeRec * edgeRecP; - -#if NIL == 0 -#define gp_IsArc(e) (e) -#define gp_IsNotArc(e) (!(e)) -#define gp_GetFirstEdge(theGraph) (2) -#elif NIL == -1 -#define gp_IsArc(e) ((e) != NIL) -#define gp_IsNotArc(e) ((e) == NIL) -#define gp_GetFirstEdge(theGraph) (0) -#else -#error NIL must be 0 or -1 -#endif - -#define gp_EdgeInUse(theGraph, e) (gp_IsVertex(gp_GetNeighbor(theGraph, e))) -#define gp_EdgeNotInUse(theGraph, e) (gp_IsNotVertex(gp_GetNeighbor(theGraph, e))) -#define gp_EdgeIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (theGraph)->arcCapacity) -#define gp_EdgeInUseIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (((theGraph)->M + sp_GetCurrentSize((theGraph)->edgeHoles)) << 1)) - -// An edge is represented by two consecutive edge records (arcs) in the edge array E. -// If an even number, xor 1 will add one; if an odd number, xor 1 will subtract 1 -#define gp_GetTwinArc(theGraph, Arc) ((Arc) ^ 1) - -// Access to adjacency list pointers -#define gp_GetNextArc(theGraph, e) (theGraph->E[e].link[0]) -#define gp_GetPrevArc(theGraph, e) (theGraph->E[e].link[1]) -#define gp_GetAdjacentArc(theGraph, e, theLink) (theGraph->E[e].link[theLink]) - -#define gp_SetNextArc(theGraph, e, newNextArc) (theGraph->E[e].link[0] = newNextArc) -#define gp_SetPrevArc(theGraph, e, newPrevArc) (theGraph->E[e].link[1] = newPrevArc) -#define gp_SetAdjacentArc(theGraph, e, theLink, newArc) (theGraph->E[e].link[theLink] = newArc) - -// Access to vertex 'neighbor' member indicated by arc -#define gp_GetNeighbor(theGraph, e) (theGraph->E[e].neighbor) -#define gp_SetNeighbor(theGraph, e, v) (theGraph->E[e].neighbor = v) - -// Initializer for edge flags -#define gp_InitEdgeFlags(theGraph, e) (theGraph->E[e].flags = 0) - -// Definitions of and access to edge flags -#define EDGE_VISITED_MASK 1 -#define gp_GetEdgeVisited(theGraph, e) (theGraph->E[e].flags&EDGE_VISITED_MASK) -#define gp_ClearEdgeVisited(theGraph, e) (theGraph->E[e].flags &= ~EDGE_VISITED_MASK) -#define gp_SetEdgeVisited(theGraph, e) (theGraph->E[e].flags |= EDGE_VISITED_MASK) - -// The edge type is defined by bits 1-3, 2+4+8=14 -#define EDGE_TYPE_MASK 14 - -// Call gp_GetEdgeType(), then compare to one of these four possibilities -// EDGE_TYPE_CHILD - edge record is an arc to a DFS child -// EDGE_TYPE_FORWARD - edge record is an arc to a DFS descendant, not a DFS child -// EDGE_TYPE_PARENT - edge record is an arc to the DFS parent -// EDGE_TYPE_BACK - edge record is an arc to a DFS ancestor, not the DFS parent -#define EDGE_TYPE_CHILD 14 -#define EDGE_TYPE_FORWARD 10 -#define EDGE_TYPE_PARENT 6 -#define EDGE_TYPE_BACK 2 - -// EDGE_TYPE_NOTDEFINED - the edge record type has not been defined -// EDGE_TYPE_RANDOMTREE - edge record is part of a randomly generated tree -#define EDGE_TYPE_NOTDEFINED 0 -#define EDGE_TYPE_RANDOMTREE 4 - -#define gp_GetEdgeType(theGraph, e) (theGraph->E[e].flags&EDGE_TYPE_MASK) -#define gp_ClearEdgeType(theGraph, e) (theGraph->E[e].flags &= ~EDGE_TYPE_MASK) -#define gp_SetEdgeType(theGraph, e, type) (theGraph->E[e].flags |= type) -#define gp_ResetEdgeType(theGraph, e, type) \ - (theGraph->E[e].flags = (theGraph->E[e].flags & ~EDGE_TYPE_MASK) | type) - -#define EDGEFLAG_INVERTED_MASK 16 -#define gp_GetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_INVERTED_MASK) -#define gp_SetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags |= EDGEFLAG_INVERTED_MASK) -#define gp_ClearEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags &= (~EDGEFLAG_INVERTED_MASK)) -#define gp_XorEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags ^= EDGEFLAG_INVERTED_MASK) - -#define EDGEFLAG_DIRECTION_INONLY 32 -#define EDGEFLAG_DIRECTION_OUTONLY 64 -#define EDGEFLAG_DIRECTION_MASK 96 - -// Returns the direction, if any, of the edge record -#define gp_GetDirection(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_DIRECTION_MASK) - -//A direction of 0 clears directedness. Otherwise, edge record e is set -//to edgeFlag_Direction and e's twin arc is set to the opposing setting. -#define gp_SetDirection(theGraph, e, edgeFlag_Direction) \ -{ \ - if (edgeFlag_Direction == EDGEFLAG_DIRECTION_INONLY) \ - { \ - theGraph->E[e].flags |= EDGEFLAG_DIRECTION_INONLY; \ - theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_OUTONLY; \ - } \ - else if (edgeFlag_Direction == EDGEFLAG_DIRECTION_OUTONLY) \ - { \ - theGraph->E[e].flags |= EDGEFLAG_DIRECTION_OUTONLY; \ - theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_INONLY; \ - } \ - else \ - { \ - theGraph->E[e].flags &= ~(EDGEFLAG_DIRECTION_INONLY|EDGEFLAG_DIRECTION_OUTONLY); \ - theGraph->E[gp_GetTwinArc(theGraph, e)].flags &= ~EDGEFLAG_DIRECTION_MASK; \ - } \ -} - -#define gp_CopyEdgeRec(dstGraph, edst, srcGraph, esrc) (dstGraph->E[edst] = srcGraph->E[esrc]) - -/******************************************************************** - Vertex Record Definition - - This record definition provides the data members needed for the - core structural information for both vertices and virtual vertices. - Vertices are also equipped with additional information provided by - the vertexInfo structure. - - The vertices of a graph are stored in the first N locations of array V. - Virtual vertices are secondary vertices used to help represent the - main vertices in substructural components of a graph (e.g. biconnected - components). - - link[2]: the first and last edge records (arcs) in the adjacency list - of the vertex. - - index: In vertices, stores either the depth first index of a vertex or - the original array index of the vertex if the vertices of the - graph are sorted by DFI. - In virtual vertices, the index may be used to indicate the vertex - that the virtual vertex represents, unless an algorithm has some - other way of making the association (for example, the planarity - algorithms rely on biconnected components and therefore place - virtual vertices of a vertex at positions corresponding to the - DFS children of the vertex). - - flags: Bits 0-15 reserved for library; bits 16 and higher for apps - Bit 0: visited, for vertices and virtual vertices - Use in lieu of TYPE_VERTEX_VISITED in K4 algorithm - Bit 1: Obstruction type VERTEX_TYPE_SET (versus not set, i.e. VERTEX_TYPE_UNKNOWN) - Bit 2: Obstruction type qualifier RYW (set) versus RXW (clear) - Bit 3: Obstruction type qualifier high (set) versus low (clear) - ********************************************************************/ - -typedef struct -{ - int link[2]; - int index; - unsigned flags; -} vertexRec; - -typedef vertexRec * vertexRecP; - -// Accessors for vertex adjacency list links -#define gp_GetFirstArc(theGraph, v) (theGraph->V[v].link[0]) -#define gp_GetLastArc(theGraph, v) (theGraph->V[v].link[1]) -#define gp_GetArc(theGraph, v, theLink) (theGraph->V[v].link[theLink]) - -#define gp_SetFirstArc(theGraph, v, newFirstArc) (theGraph->V[v].link[0] = newFirstArc) -#define gp_SetLastArc(theGraph, v, newLastArc) (theGraph->V[v].link[1] = newLastArc) -#define gp_SetArc(theGraph, v, theLink, newArc) (theGraph->V[v].link[theLink] = newArc) - -// Vertex conversions and iteration -#if NIL == 0 -#define gp_IsVertex(v) (v) -#define gp_IsNotVertex(v) (!(v)) - -#define gp_GetFirstVertex(theGraph) (1) -#define gp_GetLastVertex(theGraph) ((theGraph)->N) -#define gp_VertexInRange(theGraph, v) ((v) <= (theGraph)->N) -#define gp_VertexInRangeDescending(theGraph, v) (v) - -#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N) -#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N) - -#define gp_IsVirtualVertex(theGraph, v) ((v) > theGraph->N) -#define gp_IsNotVirtualVertex(theGraph, v) ((v) <= theGraph->N) -#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N + 1) -#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV) -#define gp_VirtualVertexInRange(theGraph, v) ((v) <= theGraph->N + theGraph->NV) - -#elif NIL == -1 -#define gp_IsVertex(v) ((v) != NIL) -#define gp_IsNotVertex(v) ((v) == NIL) - -#define gp_GetFirstVertex(theGraph) (0) -#define gp_GetLastVertex(theGraph) ((theGraph)->N - 1) -#define gp_VertexInRange(theGraph, v) ((v) < (theGraph)->N) -#define gp_VertexInRangeDescending(theGraph, v) ((v) >= 0) - -#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N) -#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N) - -#define gp_IsVirtualVertex(theGraph, v) ((v) >= theGraph->N) -#define gp_IsNotVirtualVertex(theGraph, v) ((v) < theGraph->N) -#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N) -#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV - 1) -#define gp_VirtualVertexInRange(theGraph, v) ((v) < theGraph->N + theGraph->NV) - -#else -#error NIL must be 0 or -1 -#endif - -#define gp_GetRootFromDFSChild(theGraph, c) ((c) + theGraph->N) -#define gp_GetDFSChildFromRoot(theGraph, R) ((R) - theGraph->N) -#define gp_GetPrimaryVertexFromRoot(theGraph, R) gp_GetVertexParent(theGraph, gp_GetDFSChildFromRoot(theGraph, R)) - -#define gp_IsSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild))) -#define gp_IsNotSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexNotInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild))) - -#define gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(gp_GetVertexParent(theGraph, v)) -#define gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(gp_GetVertexParent(theGraph, v)) - -// Accessors for vertex index -#define gp_GetVertexIndex(theGraph, v) (theGraph->V[v].index) -#define gp_SetVertexIndex(theGraph, v, theIndex) (theGraph->V[v].index = theIndex) - -// Initializer for vertex flags -#define gp_InitVertexFlags(theGraph, v) (theGraph->V[v].flags = 0) - -// Definitions and accessors for vertex flags -#define VERTEX_VISITED_MASK 1 -#define gp_GetVertexVisited(theGraph, v) (theGraph->V[v].flags&VERTEX_VISITED_MASK) -#define gp_ClearVertexVisited(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_VISITED_MASK) -#define gp_SetVertexVisited(theGraph, v) (theGraph->V[v].flags |= VERTEX_VISITED_MASK) - -// The obstruction type is defined by bits 1-3, 2+4+8=14 -// Bit 1 - 2 if type set, 0 if not -// Bit 2 - 4 if Y side, 0 if X side -// Bit 3 - 8 if high, 0 if low -#define VERTEX_OBSTRUCTIONTYPE_MASK 14 - -// Call gp_GetVertexObstructionType, then compare to one of these four possibilities -// VERTEX_OBSTRUCTIONTYPE_HIGH_RXW - On the external face path between vertices R and X -// VERTEX_OBSTRUCTIONTYPE_LOW_RXW - X or on the external face path between vertices X and W -// VERTEX_OBSTRUCTIONTYPE_HIGH_RYW - On the external face path between vertices R and Y -// VERTEX_OBSTRUCTIONTYPE_LOW_RYW - Y or on the external face path between vertices Y and W -// VERTEX_OBSTRUCTIONTYPE_UNKNOWN - corresponds to all three bits off -#define VERTEX_OBSTRUCTIONTYPE_HIGH_RXW 10 -#define VERTEX_OBSTRUCTIONTYPE_LOW_RXW 2 -#define VERTEX_OBSTRUCTIONTYPE_HIGH_RYW 14 -#define VERTEX_OBSTRUCTIONTYPE_LOW_RYW 6 -#define VERTEX_OBSTRUCTIONTYPE_UNKNOWN 0 - -#define VERTEX_OBSTRUCTIONTYPE_MARKED 2 -#define VERTEX_OBSTRUCTIONTYPE_UNMARKED 0 - -#define gp_GetVertexObstructionType(theGraph, v) (theGraph->V[v].flags&VERTEX_OBSTRUCTIONTYPE_MASK) -#define gp_ClearVertexObstructionType(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_OBSTRUCTIONTYPE_MASK) -#define gp_SetVertexObstructionType(theGraph, v, type) (theGraph->V[v].flags |= type) -#define gp_ResetVertexObstructionType(theGraph, v, type) \ - (theGraph->V[v].flags = (theGraph->V[v].flags & ~VERTEX_OBSTRUCTIONTYPE_MASK) | type) - -#define gp_CopyVertexRec(dstGraph, vdst, srcGraph, vsrc) (dstGraph->V[vdst] = srcGraph->V[vsrc]) - -#define gp_SwapVertexRec(dstGraph, vdst, srcGraph, vsrc) \ - { \ - vertexRec tempV = dstGraph->V[vdst]; \ - dstGraph->V[vdst] = srcGraph->V[vsrc]; \ - srcGraph->V[vsrc] = tempV; \ - } - -/******************************************************************** - This structure defines a pair of links used by each vertex and virtual vertex - to create "short circuit" paths that eliminate unimportant vertices from - the external face, enabling more efficient traversal of the external face. - - It is also possible to embed the "short circuit" edges, but this approach - creates a better separation of concerns, imparts greater clarity, and - removes exceptionalities for handling additional fake "short circuit" edges. - - vertex[2]: The two adjacent vertices along the external face, possibly - short-circuiting paths of inactive vertices. -*/ - -typedef struct -{ - int vertex[2]; -} extFaceLinkRec; - -typedef extFaceLinkRec * extFaceLinkRecP; - -#define gp_GetExtFaceVertex(theGraph, v, link) (theGraph->extFace[v].vertex[link]) -#define gp_SetExtFaceVertex(theGraph, v, link, theVertex) (theGraph->extFace[v].vertex[link] = theVertex) - -/******************************************************************** - Vertex Info Structure Definition. - - This structure equips the primary (non-virtual) vertices with additional - information needed for lowpoint and planarity-related algorithms. - - parent: The DFI of the DFS tree parent of this vertex - leastAncestor: min(DFI of neighbors connected by backedge) - lowpoint: min(leastAncestor, min(lowpoint of DFS Children)) - - visitedInfo: enables algorithms to manage vertex visitation with more than - just a flag. For example, the planarity test flags visitation - as a step number that implicitly resets on each step, whereas - part of the planar drawing method signifies a first visitation - by storing the index of the first edge used to reach a vertex - pertinentEdge: Used by the planarity method; during Walkup, each vertex - that is directly adjacent via a back edge to the vertex v - currently being embedded will have the forward edge's index - stored in this field. During Walkdown, each vertex for which - this field is set will cause a back edge to be embedded. - Implicitly resets at each vertex step of the planarity method - pertinentRootsList: used by Walkup to store a list of child bicomp roots of - a vertex descendant of the current vertex that are pertinent - and must be merged by the Walkdown in order to embed the cycle - edges of the current vertex. Future pertinent child bicomp roots - are placed at the end of the list to ensure bicomps that are - only pertinent are processed first. - futurePertinentChild: indicates a DFS child with a lowpoint less than the - current vertex v. This member is initialized to the start of - the sortedDFSChildList and is advanced in a relaxed manner as - needed until one with a lowpoint less than v is found or until - there are no more children. - sortedDFSChildList: at the start of embedding, the list of DFS children of - this vertex is calculated in ascending order by DFI (sorted in - linear time). The list is used during Walkdown processing of - a vertex to process all of its children. It is also used in - future pertinence management when processing the ancestors of - the vertex. When a child C is merged into the same bicomp as - the vertex, it is removed from the list. - fwdArcList: at the start of embedding, the "back" edges from a vertex to - its DFS *descendants* (i.e. the forward arcs of the back edges) - are separated from the main adjacency list and placed in a - circular list until they are embedded. The list is sorted in - ascending DFI order of the descendants (in linear time). - This member indicates a node in that list. -*/ - -typedef struct -{ - int parent, leastAncestor, lowpoint; - - int visitedInfo; - - int pertinentEdge, - pertinentRoots, - futurePertinentChild, - sortedDFSChildList, - fwdArcList; -} vertexInfo; - -typedef vertexInfo * vertexInfoP; - -#define gp_GetVertexVisitedInfo(theGraph, v) (theGraph->VI[v].visitedInfo) -#define gp_SetVertexVisitedInfo(theGraph, v, theVisitedInfo) (theGraph->VI[v].visitedInfo = theVisitedInfo) - -#define gp_GetVertexParent(theGraph, v) (theGraph->VI[v].parent) -#define gp_SetVertexParent(theGraph, v, theParent) (theGraph->VI[v].parent = theParent) - -#define gp_GetVertexLeastAncestor(theGraph, v) (theGraph->VI[v].leastAncestor) -#define gp_SetVertexLeastAncestor(theGraph, v, theLeastAncestor) (theGraph->VI[v].leastAncestor = theLeastAncestor) - -#define gp_GetVertexLowpoint(theGraph, v) (theGraph->VI[v].lowpoint) -#define gp_SetVertexLowpoint(theGraph, v, theLowpoint) (theGraph->VI[v].lowpoint = theLowpoint) - -#define gp_GetVertexPertinentEdge(theGraph, v) (theGraph->VI[v].pertinentEdge) -#define gp_SetVertexPertinentEdge(theGraph, v, e) (theGraph->VI[v].pertinentEdge = e) - -#define gp_GetVertexPertinentRootsList(theGraph, v) (theGraph->VI[v].pertinentRoots) -#define gp_SetVertexPertinentRootsList(theGraph, v, pertinentRootsHead) (theGraph->VI[v].pertinentRoots = pertinentRootsHead) - -#define gp_GetVertexFirstPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, theGraph->VI[v].pertinentRoots) -#define gp_GetVertexFirstPertinentRootChild(theGraph, v) (theGraph->VI[v].pertinentRoots) -#define gp_GetVertexLastPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL)) -#define gp_GetVertexLastPertinentRootChild(theGraph, v) LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL) - -#define gp_DeleteVertexPertinentRoot(theGraph, v, R) \ - gp_SetVertexPertinentRootsList(theGraph, v, \ - LCDelete(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) - -#define gp_PrependVertexPertinentRoot(theGraph, v, R) \ - gp_SetVertexPertinentRootsList(theGraph, v, \ - LCPrepend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) - -#define gp_AppendVertexPertinentRoot(theGraph, v, R) \ - gp_SetVertexPertinentRootsList(theGraph, v, \ - LCAppend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) - -#define gp_GetVertexFuturePertinentChild(theGraph, v) (theGraph->VI[v].futurePertinentChild) -#define gp_SetVertexFuturePertinentChild(theGraph, v, theFuturePertinentChild) (theGraph->VI[v].futurePertinentChild = theFuturePertinentChild) - -// Used to advance futurePertinentChild of w to the next separated DFS child with a lowpoint less than v -// Once futurePertinentChild advances past a child, no future planarity operation could make that child -// relevant to future pertinence -#define gp_UpdateVertexFuturePertinentChild(theGraph, w, v) \ - while (gp_IsVertex(theGraph->VI[w].futurePertinentChild)) \ - { \ - /* Skip children that 1) aren't future pertinent, 2) have been merged into the bicomp with w */ \ - if (gp_GetVertexLowpoint(theGraph, theGraph->VI[w].futurePertinentChild) >= v || \ - gp_IsNotSeparatedDFSChild(theGraph, theGraph->VI[w].futurePertinentChild)) \ - { \ - theGraph->VI[w].futurePertinentChild = \ - gp_GetVertexNextDFSChild(theGraph, w, gp_GetVertexFuturePertinentChild(theGraph, w)); \ - } \ - else break; \ - } - -#define gp_GetVertexSortedDFSChildList(theGraph, v) (theGraph->VI[v].sortedDFSChildList) -#define gp_SetVertexSortedDFSChildList(theGraph, v, theSortedDFSChildList) (theGraph->VI[v].sortedDFSChildList = theSortedDFSChildList) - -#define gp_GetVertexNextDFSChild(theGraph, v, c) LCGetNext(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c) - -#define gp_AppendDFSChild(theGraph, v, c) \ - LCAppend(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c) - -#define gp_GetVertexFwdArcList(theGraph, v) (theGraph->VI[v].fwdArcList) -#define gp_SetVertexFwdArcList(theGraph, v, theFwdArcList) (theGraph->VI[v].fwdArcList = theFwdArcList) - -#define gp_CopyVertexInfo(dstGraph, dstI, srcGraph, srcI) (dstGraph->VI[dstI] = srcGraph->VI[srcI]) - -#define gp_SwapVertexInfo(dstGraph, dstPos, srcGraph, srcPos) \ - { \ - vertexInfo tempVI = dstGraph->VI[dstPos]; \ - dstGraph->VI[dstPos] = srcGraph->VI[srcPos]; \ - srcGraph->VI[srcPos] = tempVI; \ - } - -/******************************************************************** - Variables needed in embedding by Kuratowski subgraph isolator: - minorType: the type of planarity obstruction found. - v: the current vertex being processed - r: the root of the bicomp on which the Walkdown failed - x,y: stopping vertices on bicomp rooted by r - w: pertinent vertex on ext. face path below x and y - px, py: attachment points of x-y path, - z: Unused except in minors D and E (not needed in A, B, C). - - ux,dx: endpoints of unembedded edge that helps connext x with - ancestor of v - uy,dy: endpoints of unembedded edge that helps connext y with - ancestor of v - dw: descendant endpoint in unembedded edge to v - uz,dz: endpoints of unembedded edge that helps connext z with - ancestor of v (for minors B and E, not A, C, D). -*/ - -typedef struct -{ - int minorType; - int v, r, x, y, w, px, py, z; - int ux, dx, uy, dy, dw, uz, dz; -} isolatorContext; - -typedef isolatorContext * isolatorContextP; - -#define MINORTYPE_A 1 -#define MINORTYPE_B 2 -#define MINORTYPE_C 4 -#define MINORTYPE_D 8 -#define MINORTYPE_E 16 -#define MINORTYPE_E1 32 -#define MINORTYPE_E2 64 -#define MINORTYPE_E3 128 -#define MINORTYPE_E4 256 - -#define MINORTYPE_E5 512 -#define MINORTYPE_E6 1024 -#define MINORTYPE_E7 2048 - -/******************************************************************** - Graph structure definition - V : Array of vertex records (allocated size N + NV) - VI: Array of additional vertexInfo structures (allocated size N) - N : Number of primary vertices (the "order" of the graph) - NV: Number of virtual vertices (currently always equal to N) - - E : Array of edge records (edge records come in pairs and represent half edges, or arcs) - M: Number of edges (the "size" of the graph) - arcCapacity: the maximum number of edge records allowed in E (the size of E) - edgeHoles: free locations in E where edges have been deleted - - theStack: Used by various graph routines needing a stack - internalFlags: Additional state information about the graph - embedFlags: controls type of embedding (e.g. planar) - - IC: contains additional useful variables for Kuratowski subgraph isolation. - BicompRootLists: storage space for pertinent bicomp root lists that develop - during embedding - sortedDFSChildLists: storage for the sorted DFS child lists of each vertex - extFace: Array of (N + NV) external face short circuit records - - extensions: a list of extension data structures - functions: a table of function pointers that can be overloaded to provide - extension behaviors to the graph -*/ - -typedef struct -{ - vertexRecP V; - vertexInfoP VI; - int N, NV; - - edgeRecP E; - int M, arcCapacity; - stackP edgeHoles; - - stackP theStack; - int internalFlags, embedFlags; - - isolatorContext IC; - listCollectionP BicompRootLists, sortedDFSChildLists; - extFaceLinkRecP extFace; - - graphExtensionP extensions; - graphFunctionTable functions; - -} baseGraphStructure; - -typedef baseGraphStructure * graphP; - -/* Flags for graph: - FLAGS_DFSNUMBERED is set if DFSNumber() has succeeded for the graph - FLAGS_SORTEDBYDFI records whether the graph is in original vertex - order or sorted by depth first index. Successive calls to - SortVertices() toggle this bit. - FLAGS_OBSTRUCTIONFOUND is set by gp_Embed() if an embedding obstruction - was isolated in the graph returned. It is cleared by gp_Embed() - if an obstruction was not found. The flag is used by - gp_TestEmbedResultIntegrity() to decide what integrity tests to run. - FLAGS_ZEROBASEDIO is typically set by gp_Read() to indicate that the - adjacency list representation began with index 0. -*/ - -#define FLAGS_DFSNUMBERED 1 -#define FLAGS_SORTEDBYDFI 2 -#define FLAGS_OBSTRUCTIONFOUND 4 -#define FLAGS_ZEROBASEDIO 8 - -/******************************************************************** - More link structure accessors/manipulators - ********************************************************************/ - -// Definitions that enable getting the next or previous arc -// as if the adjacency list were circular, i.e. that the -// first arc and last arc were linked -#define gp_GetNextArcCircular(theGraph, e) \ - (gp_IsArc(gp_GetNextArc(theGraph, e)) ? \ - gp_GetNextArc(theGraph, e) : \ - gp_GetFirstArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor)) - -#define gp_GetPrevArcCircular(theGraph, e) \ - (gp_IsArc(gp_GetPrevArc(theGraph, e)) ? \ - gp_GetPrevArc(theGraph, e) : \ - gp_GetLastArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor)) - -// Definitions that make the cross-link binding between a vertex and an arc -// The old first or last arc should be bound to this arc by separate calls, -// e.g. see gp_AttachFirstArc() and gp_AttachLastArc() -#define gp_BindFirstArc(theGraph, v, arc) \ - { \ - gp_SetPrevArc(theGraph, arc, NIL); \ - gp_SetFirstArc(theGraph, v, arc); \ - } - -#define gp_BindLastArc(theGraph, v, arc) \ - { \ - gp_SetNextArc(theGraph, arc, NIL); \ - gp_SetLastArc(theGraph, v, arc); \ - } - -// Attaches an arc between the current binding between a vertex and its first arc -#define gp_AttachFirstArc(theGraph, v, arc) \ - { \ - if (gp_IsArc(gp_GetFirstArc(theGraph, v))) \ - { \ - gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \ - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \ - } \ - else gp_BindLastArc(theGraph, v, arc); \ - gp_BindFirstArc(theGraph, v, arc); \ - } - -// Attaches an arc between the current binding betwen a vertex and its last arc -#define gp_AttachLastArc(theGraph, v, arc) \ - { \ - if (gp_IsArc(gp_GetLastArc(theGraph, v))) \ - { \ - gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \ - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \ - } \ - else gp_BindFirstArc(theGraph, v, arc); \ - gp_BindLastArc(theGraph, v, arc); \ - } - -// Moves an arc that is in the adjacency list of v to the start of the adjacency list -#define gp_MoveArcToFirst(theGraph, v, arc) \ - if (arc != gp_GetFirstArc(theGraph, v)) \ - { \ - /* If the arc is last in the adjacency list of uparent, - then we delete it by adjacency list end management */ \ - if (arc == gp_GetLastArc(theGraph, v)) \ - { \ - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), NIL); \ - gp_SetLastArc(theGraph, v, gp_GetPrevArc(theGraph, arc)); \ - } \ - /* Otherwise, we delete the arc from the middle of the list */ \ - else \ - { \ - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \ - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \ - } \ -\ - /* Now add arc e as the new first arc of uparent. - Note that the adjacency list is non-empty at this time */ \ - gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \ - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \ - gp_BindFirstArc(theGraph, v, arc); \ - } - -// Moves an arc that is in the adjacency list of v to the end of the adjacency list -#define gp_MoveArcToLast(theGraph, v, arc) \ - if (arc != gp_GetLastArc(theGraph, v)) \ - { \ - /* If the arc is first in the adjacency list of vertex v, - then we delete it by adjacency list end management */ \ - if (arc == gp_GetFirstArc(theGraph, v)) \ - { \ - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), NIL); \ - gp_SetFirstArc(theGraph, v, gp_GetNextArc(theGraph, arc)); \ - } \ - /* Otherwise, we delete the arc from the middle of the list */ \ - else \ - { \ - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \ - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \ - } \ -\ - /* Now add the arc as the new last arc of v. - Note that the adjacency list is non-empty at this time */ \ - gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \ - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \ - gp_BindLastArc(theGraph, v, arc); \ - } - -// Methods for attaching an arc into the adjacency list or detaching an arc from it. -// The terms AddArc, InsertArc and DeleteArc are not used because the arcs are not -// inserted or added to or deleted from storage (only whole edges are inserted or deleted) -void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc); -void gp_DetachArc(graphP theGraph, int arc); - -/******************************************************************** - PERTINENT() - A vertex is pertinent in a partially processed graph if there is an - unprocessed back edge between the vertex v whose edges are currently - being processed and either the vertex or a DFS descendant D of the - vertex not in the same bicomp as the vertex. - - The vertex is either directly adjacent to v by an unembedded back edge - or there is an unembedded back edge (v, D) and the vertex is a cut - vertex in the partially processed graph along the DFS tree path from - D to v. - - Pertinence is a dynamic property that can change for a vertex after - each edge addition. In other words, a vertex can become non-pertinent - during step v as more back edges to v are embedded. - ********************************************************************/ - -#define PERTINENT(theGraph, theVertex) \ - (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) || \ - gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex))) - -#define NOTPERTINENT(theGraph, theVertex) \ - (gp_IsNotArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) && \ - gp_IsNotVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex))) - -/******************************************************************** - FUTUREPERTINENT() - A vertex is future-pertinent in a partially processed graph if - there is an unprocessed back edge between a DFS ancestor A of the - vertex v whose edges are currently being processed and either - theVertex or a DFS descendant D of theVertex not in the same bicomp - as theVertex. - - Either theVertex is directly adjacent to A by an unembedded back edge - or there is an unembedded back edge (A, D) and theVertex is a cut - vertex in the partially processed graph along the DFS tree path from - D to A. - - If no more edges are added to the partially processed graph prior to - processing the edges of A, then the vertex would be pertinent. - The addition of edges to the partially processed graph can alter - both the pertinence and future pertinence of a vertex. For example, - if the vertex is pertinent due to an unprocessed back edge (v, D1) and - future pertinent due to an unprocessed back edge (A, D2), then the - vertex may lose both its pertinence and future pertinence when edge - (v, D1) is added if D2 is in the same subtree as D1. - - Generally, pertinence and future pertinence are dynamic properties - that can change for a vertex after each edge addition. - - Note that gp_UpdateVertexFuturePertinentChild() must be called before - this macro. Since it is a statement and not a void expression, the - desired commented out version does not compile (except with special - compiler extensions not assumed by this code). - ********************************************************************/ - -#define FUTUREPERTINENT(theGraph, theVertex, v) \ - ( theGraph->VI[theVertex].leastAncestor < v || \ - (gp_IsVertex(theGraph->VI[theVertex].futurePertinentChild) && \ - theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) ) - -#define NOTFUTUREPERTINENT(theGraph, theVertex, v) \ - ( theGraph->VI[theVertex].leastAncestor >= v && \ - (gp_IsNotVertex(theGraph->VI[theVertex].futurePertinentChild) || \ - theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint >= v) ) - -// This is the definition that would be preferrable if a while loop could be a void expression -//#define FUTUREPERTINENT(theGraph, theVertex, v) -// ( theGraph->VI[theVertex].leastAncestor < v || -// ((gp_UpdateVertexFuturePertinentChild(theGraph, theVertex, v), -// gp_IsArc(theGraph->VI[theVertex].futurePertinentChild)) && -// theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) ) - -/******************************************************************** - INACTIVE() - For planarity algorithms, a vertex is inactive if it is neither pertinent - nor future pertinent. - ********************************************************************/ - -#define INACTIVE(theGraph, theVertex, v) \ - ( NOTPERTINENT(theGraph, theVertex) && \ - NOTFUTUREPERTINENT(theGraph, theVertex, v)) - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/planarity_src/c/graphUtils.c b/planarity_src/c/graphUtils.c deleted file mode 100644 index 6afc37d..0000000 --- a/planarity_src/c/graphUtils.c +++ /dev/null @@ -1,2536 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include - -#include "graphStructures.h" -#include "graph.h" - -/* Imported functions for FUNCTION POINTERS */ - -extern int _EmbeddingInitialize(graphP theGraph); -extern int _SortVertices(graphP theGraph); -extern void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); -extern void _WalkUp(graphP theGraph, int v, int e); -extern int _WalkDown(graphP theGraph, int v, int RootVertex); -extern int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); -extern void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R); -extern int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -extern int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); -extern int _MarkDFSPath(graphP theGraph, int ancestor, int descendant); -extern int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -extern int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -extern int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph); -extern int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize); -extern int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize); - -/* Internal util functions for FUNCTION POINTERS */ - -int _HideVertex(graphP theGraph, int vertex); -void _HideEdge(graphP theGraph, int arcPos); -void _RestoreEdge(graphP theGraph, int arcPos); -int _ContractEdge(graphP theGraph, int e); -int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore); -int _RestoreVertex(graphP theGraph); - -/******************************************************************** - Private functions, except exported within library - ********************************************************************/ - -void _InitIsolatorContext(graphP theGraph); -void _ClearVisitedFlags(graphP theGraph); -void _ClearVertexVisitedFlags(graphP theGraph, int); -void _ClearEdgeVisitedFlags(graphP theGraph); -int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); -void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); -int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue); -int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); - -int _HideInternalEdges(graphP theGraph, int vertex); -int _RestoreInternalEdges(graphP theGraph, int stackBottom); -int _RestoreHiddenEdges(graphP theGraph, int stackBottom); - -int _GetBicompSize(graphP theGraph, int BicompRoot); -int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot); -int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot); - -void _InitFunctionTable(graphP theGraph); - -/******************************************************************** - Private functions. - ********************************************************************/ - -void _InitVertices(graphP theGraph); -void _InitEdges(graphP theGraph); - -void _ClearGraph(graphP theGraph); - -int _GetRandomNumber(int NMin, int NMax); - -/* Private functions for which there are FUNCTION POINTERS */ - -void _InitVertexRec(graphP theGraph, int v); -void _InitVertexInfo(graphP theGraph, int v); -void _InitEdgeRec(graphP theGraph, int e); - -int _InitGraph(graphP theGraph, int N); -void _ReinitializeGraph(graphP theGraph); -int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -/******************************************************************** - gp_New() - Constructor for graph object. - Can create two graphs if restricted to no dynamic memory. - ********************************************************************/ - -graphP gp_New() -{ -graphP theGraph = (graphP) malloc(sizeof(baseGraphStructure)); - - if (theGraph != NULL) - { - theGraph->E = NULL; - theGraph->V = NULL; - theGraph->VI = NULL; - - theGraph->BicompRootLists = NULL; - theGraph->sortedDFSChildLists = NULL; - theGraph->theStack = NULL; - - theGraph->extFace = NULL; - - theGraph->edgeHoles = NULL; - - theGraph->extensions = NULL; - - _InitFunctionTable(theGraph); - - _ClearGraph(theGraph); - } - - return theGraph; -} - -/******************************************************************** - _InitFunctionTable() - - If you add functions to the function table, then they must be - initialized here, but you must also add the new function pointer - to the definition of the graphFunctionTable in graphFunctionTable.h - - Function headers for the functions used to initialize the table are - classified at the top of this file as either imported from other - compilation units (extern) or private to this compilation unit. - Search for FUNCTION POINTERS in this file to see where to add the - function header. - ********************************************************************/ - -void _InitFunctionTable(graphP theGraph) -{ - theGraph->functions.fpEmbeddingInitialize = _EmbeddingInitialize; - theGraph->functions.fpEmbedBackEdgeToDescendant = _EmbedBackEdgeToDescendant; - theGraph->functions.fpWalkUp = _WalkUp; - theGraph->functions.fpWalkDown = _WalkDown; - theGraph->functions.fpMergeBicomps = _MergeBicomps; - theGraph->functions.fpMergeVertex = _MergeVertex; - theGraph->functions.fpHandleBlockedBicomp = _HandleBlockedBicomp; - theGraph->functions.fpHandleInactiveVertex = _HandleInactiveVertex; - theGraph->functions.fpEmbedPostprocess = _EmbedPostprocess; - theGraph->functions.fpMarkDFSPath = _MarkDFSPath; - theGraph->functions.fpCheckEmbeddingIntegrity = _CheckEmbeddingIntegrity; - theGraph->functions.fpCheckObstructionIntegrity = _CheckObstructionIntegrity; - - theGraph->functions.fpInitGraph = _InitGraph; - theGraph->functions.fpReinitializeGraph = _ReinitializeGraph; - theGraph->functions.fpEnsureArcCapacity = _EnsureArcCapacity; - theGraph->functions.fpSortVertices = _SortVertices; - - theGraph->functions.fpReadPostprocess = _ReadPostprocess; - theGraph->functions.fpWritePostprocess = _WritePostprocess; - - theGraph->functions.fpHideEdge = _HideEdge; - theGraph->functions.fpRestoreEdge = _RestoreEdge; - theGraph->functions.fpHideVertex = _HideVertex; - theGraph->functions.fpRestoreVertex = _RestoreVertex; - theGraph->functions.fpContractEdge = _ContractEdge; - theGraph->functions.fpIdentifyVertices = _IdentifyVertices; -} - -/******************************************************************** - gp_InitGraph() - Allocates memory for vertex and edge records now that N is known. - The arcCapacity is set to (2 * DEFAULT_EDGE_LIMIT * N) unless it - has already been set by gp_EnsureArcCapacity() - - For V, we need 2N vertex records, N for vertices and N for virtual vertices (root copies). - - For VI, we need N vertexInfo records. - - For E, we need arcCapacity edge records. - - The BicompRootLists and sortedDFSChildLists are of size N and start out empty. - - The stack, initially empty, is made big enough for a pair of integers - per edge record (2 * arcCapacity), or 6N integers if the arcCapacity - was set below the default value. - - The edgeHoles stack, initially empty, is set to arcCapacity / 2, - which is big enough to push every edge (to indicate an edge - you only need to indicate one of its two edge records) - - Returns OK on success, NOTOK on all failures. - On NOTOK, graph extensions are freed so that the graph is - returned to the post-condition of gp_New(). - ********************************************************************/ - -int gp_InitGraph(graphP theGraph, int N) -{ - // valid params check - if (theGraph == NULL || N <= 0) - return NOTOK; - - // Should not call init a second time; use reinit - if (theGraph->N) - return NOTOK; - - return theGraph->functions.fpInitGraph(theGraph, N); -} - -int _InitGraph(graphP theGraph, int N) -{ - int Vsize, VIsize, Esize, stackSize; - - // Compute the vertex and edge capacities of the graph - theGraph->N = N; - theGraph->NV = N; - theGraph->arcCapacity = theGraph->arcCapacity > 0 ? theGraph->arcCapacity : 2*DEFAULT_EDGE_LIMIT*N; - VIsize = gp_PrimaryVertexIndexBound(theGraph); - Vsize = gp_VertexIndexBound(theGraph); - Esize = gp_EdgeIndexBound(theGraph); - - // Stack size is 2 integers per arc, or 6 integers per vertex in case of small arcCapacity - stackSize = 2 * Esize; - stackSize = stackSize < 6*N ? 6*N : stackSize; - - // Allocate memory as described above - if ((theGraph->V = (vertexRecP) calloc(Vsize, sizeof(vertexRec))) == NULL || - (theGraph->VI = (vertexInfoP) calloc(VIsize, sizeof(vertexInfo))) == NULL || - (theGraph->E = (edgeRecP) calloc(Esize, sizeof(edgeRec))) == NULL || - (theGraph->BicompRootLists = LCNew(VIsize)) == NULL || - (theGraph->sortedDFSChildLists = LCNew(VIsize)) == NULL || - (theGraph->theStack = sp_New(stackSize)) == NULL || - (theGraph->extFace = (extFaceLinkRecP) calloc(Vsize, sizeof(extFaceLinkRec))) == NULL || - (theGraph->edgeHoles = sp_New(Esize / 2)) == NULL || - 0) - { - _ClearGraph(theGraph); - return NOTOK; - } - - // Initialize memory - _InitVertices(theGraph); - _InitEdges(theGraph); - _InitIsolatorContext(theGraph); - - return OK; -} - -/******************************************************************** - _InitVertices() - ********************************************************************/ -void _InitVertices(graphP theGraph) -{ -#if NIL == 0 - memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec)); - memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo)); - memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); -#elif NIL == -1 - int v; - - memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec)); - memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo)); - memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_InitVertexFlags(theGraph, v); - -#else - int v; - - // Initialize primary vertices - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - _InitVertexRec(theGraph, v); - _InitVertexInfo(theGraph, v); - gp_SetExtFaceVertex(theGraph, v, 0, NIL); - gp_SetExtFaceVertex(theGraph, v, 1, NIL); - } - - // Initialize virtual vertices - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - { - _InitVertexRec(theGraph, v); - gp_SetExtFaceVertex(theGraph, v, 0, NIL); - gp_SetExtFaceVertex(theGraph, v, 1, NIL); - } -#endif -} - -/******************************************************************** - _InitEdges() - ********************************************************************/ -void _InitEdges(graphP theGraph) -{ -#if NIL == 0 - memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); -#elif NIL == -1 - int e, Esize; - - memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); - - Esize = gp_EdgeIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - gp_InitEdgeFlags(theGraph, e); - -#else - int e, Esize; - - Esize = gp_EdgeIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - _InitEdgeRec(theGraph, e); -#endif -} - -/******************************************************************** - gp_ReinitializeGraph() - Reinitializes a graph, restoring it to the state it was in immediately - after gp_InitGraph() processed it. - ********************************************************************/ - -void gp_ReinitializeGraph(graphP theGraph) -{ - if (theGraph == NULL || theGraph->N <= 0) - return; - - theGraph->functions.fpReinitializeGraph(theGraph); -} - -void _ReinitializeGraph(graphP theGraph) -{ - theGraph->M = 0; - theGraph->internalFlags = theGraph->embedFlags = 0; - - _InitVertices(theGraph); - _InitEdges(theGraph); - _InitIsolatorContext(theGraph); - - LCReset(theGraph->BicompRootLists); - LCReset(theGraph->sortedDFSChildLists); - sp_ClearStack(theGraph->theStack); - sp_ClearStack(theGraph->edgeHoles); -} - -/******************************************************************** - gp_GetArcCapacity() - Returns the arcCapacity of theGraph, which is twice the maximum - number of edges that can be added to the theGraph. - ********************************************************************/ -int gp_GetArcCapacity(graphP theGraph) -{ - return theGraph->arcCapacity - gp_GetFirstEdge(theGraph); -} - -/******************************************************************** - gp_EnsureArcCapacity() - This method ensures that theGraph is or will be capable of storing - at least requiredArcCapacity edge records. Two edge records are - needed per edge. - - This method is most performant when invoked immediately after - gp_New(), since it must only set the arcCapacity and then let - normal initialization to occur through gp_InitGraph(). - - This method is also a constant time operation if the graph already - has at least the requiredArcCapacity, since it will return OK - without making any structural changes. - - This method is generally more performant if it is invoked before - attaching extensions to the graph. Some extensions associate - parallel data with edge records, which is a faster operation if - the associated data is created and initialized only after the - proper arcCapacity is specified. - - If the graph has been initialized and has a lower arc capacity, - then the array of edge records is reallocated to satisfy the - requiredArcCapacity. The new array contains the old edges and - edge holes at the same locations, and all newly created edge records - are initialized. - - Also, if the arc capacity must be increased, then the - arcCapacity member of theGraph is changed and both - theStack and edgeHoles are expanded (since the sizes of both - are based on the arc capacity). - - Extensions that add to data associated with edges must overload - this method to ensure capacity in the parallel extension data - structures. An extension can return NOTOK if it does not - support arc capacity expansion. The extension function will - not be called if arcCapacity is expanded before the graph is - initialized, and it is assumed that extensions will allocate - parallel data structures according to the arc capacity. - - If an extension supports arc capacity expansion, then higher - performance can be obtained by using the method of unhooking - the initializers for individual edge records before invoking - the superclass version of fpEnsureArcCapacity(). Ideally, - application authors should ensure the proper arc capacity before - attaching extensions to achieve better performance. - - Returns NOTOK on failure to reallocate the edge record array to - satisfy the requiredArcCapacity, or if the requested - capacity is odd - OK if reallocation is not required or if reallocation succeeds - ********************************************************************/ -int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ - if (theGraph == NULL || requiredArcCapacity <= 0) - return NOTOK; - - // Train callers to only ask for an even number of arcs, since - // two are required per edge or directed edge. - if (requiredArcCapacity & 1) - return NOTOK; - - if (theGraph->arcCapacity >= requiredArcCapacity) - return OK; - - // In the special case where gp_InitGraph() has not yet been called, - // we can simply set the higher arcCapacity since normal initialization - // will then allocate the correct number of edge records. - if (theGraph->N == 0) - { - theGraph->arcCapacity = requiredArcCapacity; - return OK; - } - - // Try to expand the arc capacity - return theGraph->functions.fpEnsureArcCapacity(theGraph, requiredArcCapacity); -} - -int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ -stackP newStack; -int e, Esize = gp_EdgeIndexBound(theGraph), - newEsize = gp_GetFirstEdge(theGraph) + requiredArcCapacity; - - // If the new size is less than or equal to the old size, then - // the graph already has the required arc capacity - if (newEsize <= Esize) - return OK; - - // Expand theStack - if (sp_GetCapacity(theGraph->theStack) < 2 * requiredArcCapacity) - { - int stackSize = 2 * requiredArcCapacity; - - if (stackSize < 6*theGraph->N) - { - // NOTE: Since this routine only makes the stack bigger, this - // calculation is not needed here because we already ensured - // we had stack capacity of the greater of 2*arcs and 6*N - // But we do it for clarity and consistency (e.g. so this rule - // is not forgotten whenever a "SetArcCapacity" method or a - // "reduceArcCapacity" method is added) - stackSize = 6*theGraph->N; - } - - if ((newStack = sp_New(stackSize)) == NULL) - return NOTOK; - - sp_CopyContent(newStack, theGraph->theStack); - sp_Free(&theGraph->theStack); - theGraph->theStack = newStack; - } - - // Expand edgeHoles - if ((newStack = sp_New(requiredArcCapacity / 2)) == NULL) - return NOTOK; - - sp_CopyContent(newStack, theGraph->edgeHoles); - sp_Free(&theGraph->edgeHoles); - theGraph->edgeHoles = newStack; - - // Reallocate the edgeRec array to the new size, - theGraph->E = (edgeRecP) realloc(theGraph->E, newEsize*sizeof(edgeRec)); - if (theGraph->E == NULL) - return NOTOK; - - // Initialize the new edge records - for (e = Esize; e < newEsize; e++) - _InitEdgeRec(theGraph, e); - - // The new arcCapacity has been successfully achieved - theGraph->arcCapacity = requiredArcCapacity; - return OK; -} - -/******************************************************************** - _InitVertexRec() - Sets the fields in a single vertex record to initial values - ********************************************************************/ - -void _InitVertexRec(graphP theGraph, int v) -{ - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - gp_SetVertexIndex(theGraph, v, NIL); - gp_InitVertexFlags(theGraph, v); -} - -/******************************************************************** - _InitVertexInfo() - Sets the fields in a single vertex record to initial values - ********************************************************************/ - -void _InitVertexInfo(graphP theGraph, int v) -{ - gp_SetVertexParent(theGraph, v, NIL); - gp_SetVertexLeastAncestor(theGraph, v, NIL); - gp_SetVertexLowpoint(theGraph, v, NIL); - - gp_SetVertexVisitedInfo(theGraph, v, NIL); - gp_SetVertexPertinentEdge(theGraph, v, NIL); - gp_SetVertexPertinentRootsList(theGraph, v, NIL); - gp_SetVertexFuturePertinentChild(theGraph, v, NIL); - gp_SetVertexSortedDFSChildList(theGraph, v, NIL); - gp_SetVertexFwdArcList(theGraph, v, NIL); -} - -/******************************************************************** - _InitEdgeRec() - Sets the fields in a single edge record structure to initial values - ********************************************************************/ - -void _InitEdgeRec(graphP theGraph, int e) -{ - gp_SetNeighbor(theGraph, e, NIL); - gp_SetPrevArc(theGraph, e, NIL); - gp_SetNextArc(theGraph, e, NIL); - gp_InitEdgeFlags(theGraph, e); -} - -/******************************************************************** - _InitIsolatorContext() - ********************************************************************/ - -void _InitIsolatorContext(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - IC->minorType = 0; - IC->v = IC->r = IC->x = IC->y = IC->w = IC->px = IC->py = IC->z = - IC->ux = IC->dx = IC->uy = IC->dy = IC->dw = IC->uz = IC->dz = NIL; -} - -/******************************************************************** - _ClearVisitedFlags() - ********************************************************************/ - -void _ClearVisitedFlags(graphP theGraph) -{ - _ClearVertexVisitedFlags(theGraph, TRUE); - _ClearEdgeVisitedFlags(theGraph); -} - -/******************************************************************** - _ClearVertexVisitedFlags() - ********************************************************************/ - -void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices) -{ - int v; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_ClearVertexVisited(theGraph, v); - - if (includeVirtualVertices) - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - gp_ClearVertexVisited(theGraph, v); -} - -/******************************************************************** - _ClearEdgeVisitedFlags() - ********************************************************************/ - -void _ClearEdgeVisitedFlags(graphP theGraph) -{ - int e, EsizeOccupied; - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - gp_ClearEdgeVisited(theGraph, e); -} - -/******************************************************************** - _ClearVisitedFlagsInBicomp() - - Clears the visited flag of the vertices and arcs in the bicomp rooted - by BicompRoot. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most one integer per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot) -{ -int stackBottom = sp_GetCurrentSize(theGraph->theStack); -int v, e; - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, v); - gp_ClearVertexVisited(theGraph, v); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - gp_ClearEdgeVisited(theGraph, e); - - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _ClearVisitedFlagsInOtherBicomps() - Typically, we want to clear all visited flags in the graph - (see _ClearVisitedFlags). However, in some algorithms this would be - too costly, so it is necessary to clear the visited flags only - in one bicomp (see _ClearVisitedFlagsInBicomp), then do some processing - that sets some of the flags then performs some tests. If the tests - are positive, then we can clear all the visited flags in the - other bicomps (the processing may have set the visited flags in the - one bicomp in a particular way that we want to retain, so we skip - the given bicomp). - ********************************************************************/ - -int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot) -{ - int R; - - for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) - { - if (R != BicompRoot && gp_VirtualVertexInUse(theGraph, R)) - { - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - } - } - return OK; -} - -/******************************************************************** - _ClearVisitedFlagsInUnembeddedEdges() - Unembedded edges aren't part of any bicomp yet, but it may be - necessary to clear their visited flags. - ********************************************************************/ - -void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph) -{ - int v, e; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - e = gp_GetVertexFwdArcList(theGraph, v); - while (gp_IsArc(e)) - { - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, v)) - e = NIL; - } - } -} - -/**************************************************************************** - _ClearVisitedFlagsOnPath() - This method clears the visited flags on the vertices and edges on the path - (u, v, ..., w, x) in which all vertices except the endpoints u and x - are degree 2. This method avoids performing more than constant work at the - path endpoints u and x, so the total work is on the order of the path length. - - Returns OK on success, NOTOK on internal failure - ****************************************************************************/ - -int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x) -{ - int e, eTwin; - - // We want to exit u from e, but we get eTwin first here in order to avoid - // work, in case the degree of u is greater than 2. - eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u); - if (gp_IsNotArc(eTwin)) - return NOTOK; - e = gp_GetTwinArc(theGraph, eTwin); - - v = u; - - do { - // Mark the vertex and the exiting edge - gp_ClearVertexVisited(theGraph, v); - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, eTwin); - - // Get the next vertex - v = gp_GetNeighbor(theGraph, e); - e = gp_GetNextArcCircular(theGraph, eTwin); - eTwin = gp_GetTwinArc(theGraph, e); - } while (v != x); - - // Mark the last vertex with 'visited' - gp_ClearVertexVisited(theGraph, x); - - return OK; -} - -/**************************************************************************** - _SetVisitedFlagsOnPath() - This method sets the visited flags on the vertices and edges on the path - (u, v, ..., w, x) in which all vertices except the endpoints u and x - are degree 2. This method avoids performing more than constant work at the - path endpoints u and x, so the total work is on the order of the path length. - - Returns OK on success, NOTOK on internal failure - ****************************************************************************/ - -int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x) -{ - int e, eTwin; - - // We want to exit u from e, but we get eTwin first here in order to avoid - // work, in case the degree of u is greater than 2. - eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u); - if (gp_IsNotArc(eTwin)) - return NOTOK; - e = gp_GetTwinArc(theGraph, eTwin); - - v = u; - - do { - // Mark the vertex and the exiting edge - gp_SetVertexVisited(theGraph, v); - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, eTwin); - - // Get the next vertex - v = gp_GetNeighbor(theGraph, e); - e = gp_GetNextArcCircular(theGraph, eTwin); - eTwin = gp_GetTwinArc(theGraph, e); - } while (v != x); - - // Mark the last vertex with 'visited' - gp_SetVertexVisited(theGraph, x); - - return OK; -} - -/******************************************************************** - _FillVertexVisitedInfoInBicomp() - - Places the FillValue into the visitedInfo of the non-virtual vertices - in the bicomp rooted by BicompRoot. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most one integer per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue) -{ -int v, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, v); - - if (gp_IsNotVirtualVertex(theGraph, v)) - gp_SetVertexVisitedInfo(theGraph, v, FillValue); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _ClearVertexTypeInBicomp() - - Clears the 'obstruction type' bits for each vertex in the bicomp - rooted by BicompRoot. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most one integer per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - gp_ClearVertexObstructionType(theGraph, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _ClearGraph() - Clears all memory used by the graph, restoring it to the state it - was in immediately after gp_New() created it. - ********************************************************************/ - -void _ClearGraph(graphP theGraph) -{ - if (theGraph->V != NULL) - { - free(theGraph->V); - theGraph->V = NULL; - } - if (theGraph->VI != NULL) - { - free(theGraph->VI); - theGraph->V = NULL; - } - if (theGraph->E != NULL) - { - free(theGraph->E); - theGraph->E = NULL; - } - - theGraph->N = 0; - theGraph->NV = 0; - theGraph->M = 0; - theGraph->arcCapacity = 0; - theGraph->internalFlags = 0; - theGraph->embedFlags = 0; - - _InitIsolatorContext(theGraph); - - LCFree(&theGraph->BicompRootLists); - LCFree(&theGraph->sortedDFSChildLists); - - sp_Free(&theGraph->theStack); - - if (theGraph->extFace != NULL) - { - free(theGraph->extFace); - theGraph->extFace = NULL; - } - - sp_Free(&theGraph->edgeHoles); - - gp_FreeExtensions(theGraph); -} - -/******************************************************************** - gp_Free() - Frees G and V, then the graph record. Then sets your pointer to NULL - (so you must pass the address of your pointer). - ********************************************************************/ - -void gp_Free(graphP *pGraph) -{ - if (pGraph == NULL) return; - if (*pGraph == NULL) return; - - _ClearGraph(*pGraph); - - free(*pGraph); - *pGraph = NULL; -} - -/******************************************************************** - gp_CopyAdjacencyLists() - Copies the adjacency lists from the srcGraph to the dstGraph. - This method intentionally copies only the adjacency lists of the - first N vertices, so the adjacency lists of virtual vertices are - excluded (unless the caller temporarily resets the value of N to - include NV). - - Returns OK on success, NOTOK on failures, e.g. if the two graphs - have different orders N or if the arcCapacity of dstGraph cannot - be increased to match that of srcGraph. - ********************************************************************/ -int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph) -{ - int v, e, EsizeOccupied; - - if (dstGraph == NULL || srcGraph == NULL) - return NOTOK; - - if (dstGraph->N != srcGraph->N || dstGraph->N == 0) - return NOTOK; - - if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK) - return NOTOK; - - // Copy the links that hook each owning vertex to its adjacency list - for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++) - { - gp_SetFirstArc(dstGraph, v, gp_GetFirstArc(srcGraph, v)); - gp_SetLastArc(dstGraph, v, gp_GetLastArc(srcGraph, v)); - } - - // Copy the adjacency links and neighbor pointers for each arc - EsizeOccupied = gp_EdgeInUseIndexBound(srcGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - { - gp_SetNeighbor(dstGraph, e, gp_GetNeighbor(srcGraph, e)); - gp_SetNextArc(dstGraph, e, gp_GetNextArc(srcGraph, e)); - gp_SetPrevArc(dstGraph, e, gp_GetPrevArc(srcGraph, e)); - } - - // Tell the dstGraph how many edges it now has and where the edge holes are - dstGraph->M = srcGraph->M; - sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles); - - return OK; -} - -/******************************************************************** - gp_CopyGraph() - Copies the content of the srcGraph into the dstGraph. The dstGraph - must have been previously initialized with the same number of - vertices as the srcGraph (e.g. gp_InitGraph(dstGraph, srcGraph->N). - - Returns OK for success, NOTOK for failure. - ********************************************************************/ - -int gp_CopyGraph(graphP dstGraph, graphP srcGraph) -{ -int v, e, Esize; - - // Parameter checks - if (dstGraph == NULL || srcGraph == NULL) - { - return NOTOK; - } - - // The graphs need to be the same order and initialized - if (dstGraph->N != srcGraph->N || dstGraph->N == 0) - { - return NOTOK; - } - - // Ensure dstGraph has the required arc capacity; this expands - // dstGraph if needed, but does not contract. An error is only - // returned if the expansion fails. - if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK) - { - return NOTOK; - } - - // Copy the primary vertices. Augmentations to vertices created - // by extensions are copied below by gp_CopyExtensions() - for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++) - { - gp_CopyVertexRec(dstGraph, v, srcGraph, v); - gp_CopyVertexInfo(dstGraph, v, srcGraph, v); - gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0)); - gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1)); - } - - // Copy the virtual vertices. Augmentations to virtual vertices created - // by extensions are copied below by gp_CopyExtensions() - for (v = gp_GetFirstVirtualVertex(srcGraph); gp_VirtualVertexInRange(srcGraph, v); v++) - { - gp_CopyVertexRec(dstGraph, v, srcGraph, v); - gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0)); - gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1)); - } - - // Copy the basic EdgeRec structures. Augmentations to the edgeRec structure - // created by extensions are copied below by gp_CopyExtensions() - Esize = gp_EdgeIndexBound(srcGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - gp_CopyEdgeRec(dstGraph, e, srcGraph, e); - - // Give the dstGraph the same size and intrinsic properties - dstGraph->N = srcGraph->N; - dstGraph->NV = srcGraph->NV; - dstGraph->M = srcGraph->M; - dstGraph->internalFlags = srcGraph->internalFlags; - dstGraph->embedFlags = srcGraph->embedFlags; - - dstGraph->IC = srcGraph->IC; - - LCCopy(dstGraph->BicompRootLists, srcGraph->BicompRootLists); - LCCopy(dstGraph->sortedDFSChildLists, srcGraph->sortedDFSChildLists); - sp_Copy(dstGraph->theStack, srcGraph->theStack); - sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles); - - // Copy the set of extensions, which includes copying the - // extension data as well as the function overload tables - if (gp_CopyExtensions(dstGraph, srcGraph) != OK) - return NOTOK; - - // Copy the graph's function table, which has the pointers to - // the most recent extension overloads of each function (or - // the original function pointer if a particular function has - // not been overloaded). - // This must be done after copying the extension because the - // first step of copying the extensions is to delete the - // dstGraph extensions, which clears its function table. - // Therefore, no good to assign the srcGraph functions *before* - // copying the extensions because the assignment would be wiped out - // This, in turn, means that the DupContext function of an extension - // *cannot* depend on any extension function overloads; the extension - // must directly invoke extension functions only. - dstGraph->functions = srcGraph->functions; - - return OK; -} - -/******************************************************************** - gp_DupGraph() - ********************************************************************/ - -graphP gp_DupGraph(graphP theGraph) -{ -graphP result; - - if ((result = gp_New()) == NULL) return NULL; - - if (gp_InitGraph(result, theGraph->N) != OK || - gp_CopyGraph(result, theGraph) != OK) - { - gp_Free(&result); - return NULL; - } - - return result; -} - -/******************************************************************** - gp_CreateRandomGraph() - - Creates a randomly generated graph. First a tree is created by - connecting each vertex to some successor. Then a random number of - additional random edges are added. If an edge already exists, then - we retry until a non-existent edge is picked. - - This function assumes the caller has already called srand(). - - Returns OK on success, NOTOK on failure - ********************************************************************/ - -int gp_CreateRandomGraph(graphP theGraph) -{ -int N, M, u, v, m; - - N = theGraph->N; - -/* Generate a random tree; note that this method virtually guarantees - that the graph will be renumbered, but it is linear time. - Also, we are not generating the DFS tree but rather a tree - that simply ensures the resulting random graph is connected. */ - - for (v = gp_GetFirstVertex(theGraph)+1; gp_VertexInRange(theGraph, v); v++) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v-1); - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - } - -/* Generate a random number of additional edges - (actually, leave open a small chance that no - additional edges will be added). */ - - M = _GetRandomNumber(7*N/8, theGraph->arcCapacity/2); - - if (M > N*(N-1)/2) - M = N*(N-1)/2; - - for (m = N-1; m < M; m++) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)-1); - v = _GetRandomNumber(u+1, gp_GetLastVertex(theGraph)); - - // If the edge (u,v) exists, decrement eIndex to try again - if (gp_IsNeighbor(theGraph, u, v)) - m--; - - // If the edge (u,v) doesn't exist, add it - else - { - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - _GetRandomNumber() - This function generates a random number between NMin and NMax - inclusive. It assumes that the caller has called srand(). - It calls rand(), but before truncating to the proper range, - it adds the high bits of the rand() result into the low bits. - The result of this is that the randomness appearing in the - truncated bits also has an affect on the non-truncated bits. - ********************************************************************/ - -int _GetRandomNumber(int NMin, int NMax) -{ -int N = rand(); - - if (NMax < NMin) return NMin; - - N += ((N&0xFFFF0000)>>16); - N += ((N&0x0000FF00)>>8); - N &= 0x7FFFFFF; - N %= (NMax-NMin+1); - return N+NMin; -} - -/******************************************************************** - _getUnprocessedChild() - Support routine for gp_Create RandomGraphEx(), this function - obtains a child of the given vertex in the randomly generated - tree that has not yet been processed. NIL is returned if the - given vertex has no unprocessed children - - ********************************************************************/ - -int _getUnprocessedChild(graphP theGraph, int parent) -{ -int e = gp_GetFirstArc(theGraph, parent); -int eTwin = gp_GetTwinArc(theGraph, e); -int child = gp_GetNeighbor(theGraph, e); - - // The tree edges were added to the beginning of the adjacency list, - // and we move processed tree edge records to the end of the list, - // so if the immediate next arc (edge record) is not a tree edge - // then we return NIL because the vertex has no remaining - // unprocessed children - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED) - return NIL; - - // If the child has already been processed, then all children - // have been pushed to the end of the list, and we have just - // encountered the first child we processed, so there are no - // remaining unprocessed children */ - if (gp_GetEdgeVisited(theGraph, e)) - return NIL; - - // We have found an edge leading to an unprocessed child, so - // we mark it as processed so that it doesn't get returned - // again in future iterations. - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, eTwin); - - // Now we move the edge record in the parent vertex to the end - // of the adjacency list of that vertex. - gp_MoveArcToLast(theGraph, parent, e); - - // Now we move the edge record in the child vertex to the - // end of the adjacency list of the child. - gp_MoveArcToLast(theGraph, child, eTwin); - - // Now we set the child's parent and return the child. - gp_SetVertexParent(theGraph, child, parent); - - return child; -} - -/******************************************************************** - _hasUnprocessedChild() - Support routine for gp_Create RandomGraphEx(), this function - obtains a child of the given vertex in the randomly generated - tree that has not yet been processed. False (0) is returned - unless the given vertex has an unprocessed child. - ********************************************************************/ - -int _hasUnprocessedChild(graphP theGraph, int parent) -{ -int e = gp_GetFirstArc(theGraph, parent); - - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED) - return 0; - - if (gp_GetEdgeVisited(theGraph, e)) - return 0; - - return 1; -} - -/******************************************************************** - gp_CreateRandomGraphEx() - Given a graph structure with a pre-specified number of vertices N, - this function creates a graph with the specified number of edges. - - If numEdges <= 3N-6, then the graph generated is planar. If - numEdges is larger, then a maximal planar graph is generated, then - (numEdges - 3N + 6) additional random edges are added. - - This function assumes the caller has already called srand(). - ********************************************************************/ - -int gp_CreateRandomGraphEx(graphP theGraph, int numEdges) -{ -int N, arc, M, root, v, c, p, last, u, e, EsizeOccupied; - - N = theGraph->N; - - if (numEdges > theGraph->arcCapacity/2) - numEdges = theGraph->arcCapacity/2; - -/* Generate a random tree. */ - - for (v = gp_GetFirstVertex(theGraph)+1; gp_VertexInRange(theGraph, v); v++) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v-1); - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - - else - { - arc = 2*theGraph->M - 2; - gp_SetEdgeType(theGraph, arc, EDGE_TYPE_RANDOMTREE); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, arc), EDGE_TYPE_RANDOMTREE); - gp_ClearEdgeVisited(theGraph, arc); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, arc)); - } - } - -/* Add edges up to the limit or until the graph is maximal planar. */ - - M = numEdges <= 3*N - 6 ? numEdges : 3*N - 6; - - root = 0; - v = last = _getUnprocessedChild(theGraph, root); - - while (v != root && theGraph->M < M) - { - c = _getUnprocessedChild(theGraph, v); - - if (gp_IsVertex(c)) - { - if (last != v) - { - if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) - return NOTOK; - } - - if (gp_AddEdge(theGraph, root, 1, c, 1) != OK) - return NOTOK; - - v = last = c; - } - - else - { - p = gp_GetVertexParent(theGraph, v); - while (gp_IsVertex(p) && gp_IsNotVertex(c = _getUnprocessedChild(theGraph, p))) - { - v = p; - p = gp_GetVertexParent(theGraph, v); - if (gp_IsVertex(p) && p != root) - { - if (gp_AddEdge(theGraph, last, 1, p, 1) != OK) - return NOTOK; - } - } - - if (gp_IsVertex(p)) - { - if (p == root) - { - if (gp_AddEdge(theGraph, v, 1, c, 1) != OK) - return NOTOK; - - if (v != last) - { - if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) - return NOTOK; - } - } - else - { - if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) - return NOTOK; - } - - if (p != root) - { - if (gp_AddEdge(theGraph, root, 1, c, 1) != OK) - return NOTOK; - last = c; - } - - v = c; - } - } - } - -/* Add additional edges if the limit has not yet been reached. */ - - while (theGraph->M < numEdges) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)); - v = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)); - - if (u != v && !gp_IsNeighbor(theGraph, u, v)) - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - } - -/* Clear the edge types back to 'unknown' */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = 0; e < EsizeOccupied; e++) - { - gp_ClearEdgeType(theGraph, e); - gp_ClearEdgeVisited(theGraph, e); - } - -/* Put all DFSParent indicators back to NIL */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_SetVertexParent(theGraph, v, NIL); - - return OK; -} - -/******************************************************************** - gp_IsNeighbor() - - Checks whether v is already in u's adjacency list, i.e. does the arc - u -> v exist. - If there is an edge record for v in u's list, but it is marked INONLY, - then it represents the arc v->u but not u->v, so it is ignored. - - Returns TRUE or FALSE. - ********************************************************************/ - -int gp_IsNeighbor(graphP theGraph, int u, int v) -{ -int e = gp_GetFirstArc(theGraph, u); - - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) == v) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) - return TRUE; - } - e = gp_GetNextArc(theGraph, e); - } - return FALSE; -} - -/******************************************************************** - gp_GetNeighborEdgeRecord() - Searches the adjacency list of u to obtains the edge record for v. - - NOTE: The caller should check whether the edge record is INONLY; - This method returns any edge record representing a connection - between vertices u and v, so this method can return an - edge record even if gp_IsNeighbor(theGraph, u, v) is false (0). - To filter out INONLY edge records, use gp_GetDirection() on - the edge record returned by this method. - - Returns NIL if there is no edge record indicating v in u's adjacency - list, or the edge record location otherwise. - ********************************************************************/ - -int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v) -{ -int e; - - if (gp_IsNotVertex(u) || gp_IsNotVertex(v)) - return NIL + NOTOK - NOTOK; - - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) == v) - return e; - - e = gp_GetNextArc(theGraph, e); - } - return NIL; -} - -/******************************************************************** - gp_GetVertexDegree() - - Counts the number of edge records in the adjacency list of a given - vertex V. - - Note: For digraphs, this method returns the total degree of the - vertex, including outward arcs (undirected and OUTONLY) - as well as INONLY arcs. Other functions are defined to get - the in-degree or out-degree of the vertex. - - Note: This function determines the degree by counting. An extension - could cache the degree value of each vertex and update the - cached value as edges are added and deleted. - ********************************************************************/ - -int gp_GetVertexDegree(graphP theGraph, int v) -{ -int e, degree; - - if (theGraph==NULL || gp_IsNotVertex(v)) - return 0 + NOTOK - NOTOK; - - degree = 0; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - degree++; - e = gp_GetNextArc(theGraph, e); - } - - return degree; -} - -/******************************************************************** - gp_GetVertexInDegree() - - Counts the number of edge records in the adjacency list of a given - vertex V that represent arcs from another vertex into V. - This includes undirected edges and INONLY arcs, so it only excludes - edges records that are marked as OUTONLY arcs. - - Note: This function determines the in-degree by counting. An extension - could cache the in-degree value of each vertex and update the - cached value as edges are added and deleted. - ********************************************************************/ - -int gp_GetVertexInDegree(graphP theGraph, int v) -{ -int e, degree; - - if (theGraph==NULL || gp_IsNotVertex(v)) - return 0 + NOTOK - NOTOK; - - degree = 0; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_OUTONLY) - degree++; - e = gp_GetNextArc(theGraph, e); - } - - return degree; -} - -/******************************************************************** - gp_GetVertexOutDegree() - - Counts the number of edge records in the adjacency list of a given - vertex V that represent arcs from V to another vertex. - This includes undirected edges and OUTONLY arcs, so it only excludes - edges records that are marked as INONLY arcs. - - Note: This function determines the out-degree by counting. An extension - could cache the out-degree value of each vertex and update the - cached value as edges are added and deleted. - ********************************************************************/ - -int gp_GetVertexOutDegree(graphP theGraph, int v) -{ -int e, degree; - - if (theGraph==NULL || gp_IsNotVertex(v)) - return 0 + NOTOK - NOTOK; - - degree = 0; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) - degree++; - e = gp_GetNextArc(theGraph, e); - } - - return degree; -} - -/******************************************************************** - gp_AttachArc() - - This routine adds newArc into v's adjacency list at a position - adjacent to the edge record for e, either before or after e, - depending on link. If e is not an arc (e.g. if e is NIL), - then link is assumed to indicate whether the new arc is to be - placed at the beginning or end of v's adjacency list. - - NOTE: The caller can pass NIL for v if e is not NIL, since the - vertex is implied (gp_GetNeighbor(theGraph, eTwin)) - - The arc is assumed to already exist in the data structure (i.e. - the storage of edges), as only a whole edge (two arcs) can be - inserted into or deleted from the data structure. Hence there is - no such thing as gp_InsertArc() or gp_DeleteArc(). - - See also gp_DetachArc(), gp_InsertEdge() and gp_DeleteEdge() - ********************************************************************/ - -void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc) -{ - if (gp_IsArc(e)) - { - int e2 = gp_GetAdjacentArc(theGraph, e, link); - - // e's link is newArc, and newArc's 1^link is e - gp_SetAdjacentArc(theGraph, e, link, newArc); - gp_SetAdjacentArc(theGraph, newArc, 1^link, e); - - // newArcs's link is e2 - gp_SetAdjacentArc(theGraph, newArc, link, e2); - - // if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc - if (gp_IsArc(e2)) - gp_SetAdjacentArc(theGraph, e2, 1^link, newArc); - else - gp_SetArc(theGraph, v, 1^link, newArc); - } - else - { - int e2 = gp_GetArc(theGraph, v, link); - - // v's link is newArc, and newArc's 1^link is NIL - gp_SetArc(theGraph, v, link, newArc); - gp_SetAdjacentArc(theGraph, newArc, 1^link, NIL); - - // newArcs's elink is e2 - gp_SetAdjacentArc(theGraph, newArc, link, e2); - - // if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc - if (gp_IsArc(e2)) - gp_SetAdjacentArc(theGraph, e2, 1^link, newArc); - else - gp_SetArc(theGraph, v, 1^link, newArc); - } -} - -/**************************************************************************** - gp_DetachArc() - - This routine detaches arc from its adjacency list, but it does not delete - it from the data structure (only a whole edge can be deleted). - - Some algorithms must temporarily detach an edge, perform some calculation, - and eventually put the edge back. This routine supports that operation. - The neighboring adjacency list nodes are cross-linked, but the two link - members of the arc are retained, so the arc can be reattached later by - invoking _RestoreArc(). A sequence of detached arcs can only be restored - in the exact opposite order of their detachment. Thus, algorithms do not - directly use this method to implement the temporary detach/restore method. - Instead, gp_HideEdge() and gp_RestoreEdge are used, and algorithms push - edge hidden edge onto the stack. One example of this stack usage is - provided by detaching edges with gp_ContractEdge() or gp_IdentifyVertices(), - and reattaching with gp_RestoreIdentifications(), which unwinds the stack - by invoking gp_RestoreVertex(). - ****************************************************************************/ - -void gp_DetachArc(graphP theGraph, int arc) -{ - int nextArc = gp_GetNextArc(theGraph, arc), - prevArc = gp_GetPrevArc(theGraph, arc); - - if (gp_IsArc(nextArc)) - gp_SetPrevArc(theGraph, nextArc, prevArc); - else - gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), prevArc); - - if (gp_IsArc(prevArc)) - gp_SetNextArc(theGraph, prevArc, nextArc); - else - gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), nextArc); -} - -/******************************************************************** - gp_AddEdge() - Adds the undirected edge (u,v) to the graph by placing edge records - representing u into v's circular edge record list and v into u's - circular edge record list. - - upos receives the location in G where the u record in v's list will be - placed, and vpos is the location in G of the v record we placed in - u's list. These are used to initialize the short circuit links. - - ulink (0|1) indicates whether the edge record to v in u's list should - become adjacent to u by its 0 or 1 link, i.e. u[ulink] == vpos. - vlink (0|1) indicates whether the edge record to u in v's list should - become adjacent to v by its 0 or 1 link, i.e. v[vlink] == upos. - - ********************************************************************/ - -int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink) -{ -int upos, vpos; - - if (theGraph==NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || - !gp_VirtualVertexInRange(theGraph, u) || !gp_VirtualVertexInRange(theGraph, v)) - return NOTOK; - - /* We enforce the edge limit */ - - if (theGraph->M >= theGraph->arcCapacity/2) - return NONEMBEDDABLE; - - if (sp_NonEmpty(theGraph->edgeHoles)) - { - sp_Pop(theGraph->edgeHoles, vpos); - } - else - vpos = gp_EdgeInUseIndexBound(theGraph); - - upos = gp_GetTwinArc(theGraph, vpos); - - gp_SetNeighbor(theGraph, upos, v); - gp_AttachArc(theGraph, u, NIL, ulink, upos); - gp_SetNeighbor(theGraph, vpos, u); - gp_AttachArc(theGraph, v, NIL, vlink, vpos); - - theGraph->M++; - return OK; -} - -/******************************************************************** - gp_InsertEdge() - - This function adds the edge (u, v) such that the edge record added - to the adjacency list of u is adjacent to e_u and the edge record - added to the adjacency list of v is adjacent to e_v. - The direction of adjacency is given by e_ulink for e_u and e_vlink - for e_v. Specifically, the new edge will be comprised of two arcs, - n_u and n_v. In u's (v's) adjacency list, n_u (n_v) will be added - so that it is indicated by e_u's (e_v's) e_ulink (e_vlink). - If e_u (or e_v) is not an arc, then e_ulink (e_vlink) indicates - whether to prepend or append to the adjacency list for u (v). - ********************************************************************/ - -int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink, - int v, int e_v, int e_vlink) -{ -int vertMax = gp_GetLastVirtualVertex(theGraph), - edgeMax = gp_EdgeInUseIndexBound(theGraph) - 1, - upos, vpos; - - if (theGraph==NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || - u > vertMax || v > vertMax || - e_u > edgeMax || (e_u < gp_GetFirstEdge(theGraph) && gp_IsArc(e_u)) || - e_v > edgeMax || (e_v < gp_GetFirstEdge(theGraph) && gp_IsArc(e_v)) || - e_ulink < 0 || e_ulink > 1 || e_vlink < 0 || e_vlink > 1) - return NOTOK; - - if (theGraph->M >= theGraph->arcCapacity/2) - return NONEMBEDDABLE; - - if (sp_NonEmpty(theGraph->edgeHoles)) - { - sp_Pop(theGraph->edgeHoles, vpos); - } - else - vpos = gp_EdgeInUseIndexBound(theGraph); - - upos = gp_GetTwinArc(theGraph, vpos); - - gp_SetNeighbor(theGraph, upos, v); - gp_AttachArc(theGraph, u, e_u, e_ulink, upos); - - gp_SetNeighbor(theGraph, vpos, u); - gp_AttachArc(theGraph, v, e_v, e_vlink, vpos); - - theGraph->M++; - - return OK; -} - -/**************************************************************************** - gp_DeleteEdge() - - This function deletes the given edge record e and its twin, reducing the - number of edges M in the graph. - Before the e^th record is deleted, its 'nextLink' adjacency list neighbor - is collected as the return result. This is useful when iterating through - an edge list and making deletions because the nextLink arc is the 'next' - arc in the iteration, but it is hard to obtain *after* deleting e. - ****************************************************************************/ - -int gp_DeleteEdge(graphP theGraph, int e, int nextLink) -{ - // Calculate the nextArc after e so that, when e is deleted, the return result - // informs a calling loop of the next edge to be processed. - int nextArc = gp_GetAdjacentArc(theGraph, e, nextLink); - - // Delete the edge records e and eTwin from their adjacency lists. - gp_DetachArc(theGraph, e); - gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e)); - - // Clear the two edge records - // (the bit twiddle (e & ~1) chooses the lesser of e and its twin arc) -#if NIL == 0 - memset(theGraph->E + (e & ~1), NIL_CHAR, sizeof(edgeRec) << 1); -#else - _InitEdgeRec(theGraph, e); - _InitEdgeRec(theGraph, gp_GetTwinArc(theGraph, e)); -#endif - - // Now we reduce the number of edges in the data structure - theGraph->M--; - - // If records e and eTwin were not the last in the edge record array, - // then record a new hole in the edge array. */ - if (e < gp_EdgeInUseIndexBound(theGraph)) - { - sp_Push(theGraph->edgeHoles, e); - } - - // Return the previously calculated successor of e. - return nextArc; -} - -/******************************************************************** - _RestoreArc() - This routine reinserts an arc into the edge list from which it - was previously removed by gp_DetachArc(). - - The assumed processing model is that arcs will be restored in reverse - of the order in which they were hidden, i.e. it is assumed that the - hidden arcs will be pushed on a stack and the arcs will be popped - from the stack for restoration. - ********************************************************************/ - -void _RestoreArc(graphP theGraph, int arc) -{ -int nextArc = gp_GetNextArc(theGraph, arc), - prevArc = gp_GetPrevArc(theGraph, arc); - - if (gp_IsArc(nextArc)) - gp_SetPrevArc(theGraph, nextArc, arc); - else - gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc); - - if (gp_IsArc(prevArc)) - gp_SetNextArc(theGraph, prevArc, arc); - else - gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc); -} - -/******************************************************************** - gp_HideEdge() - This routine removes the two arcs of an edge from the adjacency lists - of its endpoint vertices, but does not delete them from the storage - data structure. - - Many algorithms must temporarily remove an edge, perform some - calculation, and eventually put the edge back. This routine supports - that operation. - - For each arc, the neighboring adjacency list nodes are cross-linked, - but the links in the arc are retained because they indicate the - neighbor arcs to which the arc can be reattached by gp_RestoreEdge(). - ********************************************************************/ - -void gp_HideEdge(graphP theGraph, int e) -{ - theGraph->functions.fpHideEdge(theGraph, e); -} - -void _HideEdge(graphP theGraph, int e) -{ - gp_DetachArc(theGraph, e); - gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e)); -} - -/******************************************************************** - gp_RestoreEdge() - This routine reinserts two two arcs of an edge into the adjacency - lists of the edge's endpoints, the arcs having been previously - removed by gp_HideEdge(). - - The assumed processing model is that edges will be restored in - reverse of the order in which they were hidden, i.e. it is assumed - that the hidden edges will be pushed on a stack and the edges will - be popped from the stack for restoration. - - Note: Since both arcs of an edge are restored, only one arc need - be pushed on the stack for restoration. This routine - restores the two arcs in the opposite order from the order - in which they are hidden by gp_HideEdge(). - ********************************************************************/ - -void gp_RestoreEdge(graphP theGraph, int e) -{ - theGraph->functions.fpRestoreEdge(theGraph, e); -} - -void _RestoreEdge(graphP theGraph, int e) -{ - _RestoreArc(theGraph, gp_GetTwinArc(theGraph, e)); - _RestoreArc(theGraph, e); -} - -/******************************************************************** - _HideInternalEdges() - Pushes onto the graph's stack and hides all arc nodes of the vertex - except the first and last arcs in the adjacency list of the vertex. - This method is typically called on a vertex that is on the external - face of a biconnected component, because the first and last arcs are - the ones that attach the vertex to the external face cycle, and any - other arcs in the adjacency list are inside that cycle. - - This method uses the stack. The caller is expected to clear the stack - or save the stack size before invocation, since the stack size is - needed to _RestoreInternalEdges(). - ********************************************************************/ - -int _HideInternalEdges(graphP theGraph, int vertex) -{ -int e = gp_GetFirstArc(theGraph, vertex); - - // If the vertex adjacency list is empty or if it contains - // only one edge, then there are no *internal* edges to hide - if (e == gp_GetLastArc(theGraph, vertex)) - return OK; - - // Start with the first internal edge - e = gp_GetNextArc(theGraph, e); - - // Cycle through all the edges, pushing each except stop - // before pushing the last edge, which is not internal - while (e != gp_GetLastArc(theGraph, vertex)) - { - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - e = gp_GetNextArc(theGraph, e); - } - - return OK; -} - -/******************************************************************** - _RestoreInternalEdges() - Reverses the effects of _HideInternalEdges() - ********************************************************************/ - -int _RestoreInternalEdges(graphP theGraph, int stackBottom) -{ - return _RestoreHiddenEdges(theGraph, stackBottom); -} - -/******************************************************************** - _RestoreHiddenEdges() - - Each entry on the stack, down to stackBottom, is assumed to be an - edge record (arc) pushed in concert with invoking gp_HideEdge(). - Each edge is restored using gp_RestoreEdge() in exact reverse of the - hiding order. The stack is reduced in size to stackBottom. - - Returns OK on success, NOTOK on internal failure. - ********************************************************************/ - -int _RestoreHiddenEdges(graphP theGraph, int stackBottom) -{ - int e; - - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, e); - if (gp_IsNotArc(e)) - return NOTOK; - gp_RestoreEdge(theGraph, e); - } - - return OK; -} - -/******************************************************************** - gp_HideVertex() - - Pushes onto the graph's stack and hides all arc nodes of the vertex. - Additional integers are then pushed so that the result is reversible - by gp_RestoreVertex(). See that method for details on the expected - stack segment. - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_HideVertex(graphP theGraph, int vertex) -{ - if (gp_IsNotVertex(vertex)) - return NOTOK; - - return theGraph->functions.fpHideVertex(theGraph, vertex); -} - -int _HideVertex(graphP theGraph, int vertex) -{ - int hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack); - int e = gp_GetFirstArc(theGraph, vertex); - - // Cycle through all the edges, pushing and hiding each - while (gp_IsArc(e)) - { - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - e = gp_GetNextArc(theGraph, e); - } - - // Push the additional integers needed by gp_RestoreVertex() - sp_Push(theGraph->theStack, hiddenEdgeStackBottom); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, vertex); - - return OK; -} - -/******************************************************************** - gp_ContractEdge() - - Contracts the edge e=(u,v). This hides the edge (both e and its - twin arc), and it also identifies vertex v with u. - See gp_IdentifyVertices() for further details. - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_ContractEdge(graphP theGraph, int e) -{ - if (gp_IsNotArc(e)) - return NOTOK; - - return theGraph->functions.fpContractEdge(theGraph, e); -} - -int _ContractEdge(graphP theGraph, int e) -{ - int eBefore, u, v; - - if (gp_IsNotArc(e)) - return NOTOK; - - u = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, e)); - v = gp_GetNeighbor(theGraph, e); - - eBefore = gp_GetNextArc(theGraph, e); - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - - return gp_IdentifyVertices(theGraph, u, v, eBefore); -} - -/******************************************************************** - gp_IdentifyVertices() - - Identifies vertex v with vertex u by transferring all adjacencies - of v to u. Any duplicate edges are removed as described below. - The non-duplicate edges of v are added to the adjacency list of u - without disturbing their relative order, and they are added before - the edge record eBefore in u's list. If eBefore is NIL, then the - edges are simply appended to u's list. - - If u and v are adjacent, then gp_HideEdge() is invoked to remove - the edge e=(u,v). Then, the edges of v that indicate neighbors of - u are also hidden. This is done by setting the visited flags of - u's neighbors, then traversing the adjacency list of v. For each - visited neighbor of v, the edge is hidden because it would duplicate - an adjacency already expressed in u's list. Finally, the remaining - edges of v are moved to u's list, and each twin arc is adjusted - to indicate u as a neighbor rather than v. - - This routine assumes that the visited flags are clear beforehand, - and visited flag settings made herein are cleared before returning. - - The following are pushed, in order, onto the graph's built-in stack: - 1) an integer for each hidden edge - 2) the stack size before any hidden edges were pushed - 3) six integers that indicate u, v and the edges moved from v to u - - An algorithm that identifies a series of vertices, either through - directly calling this method or via gp_ContractEdge(), can unwind - the identifications using gp_RestoreIdentifications(), which - invokes gp_RestoreVertex() repeatedly. - - Returns OK on success, NOTOK on internal failure - ********************************************************************/ - -int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore) -{ - return theGraph->functions.fpIdentifyVertices(theGraph, u, v, eBefore); -} - -int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore) -{ - int e = gp_GetNeighborEdgeRecord(theGraph, u, v); - int hiddenEdgeStackBottom, eBeforePred; - - // If the vertices are adjacent, then the identification is - // essentially an edge contraction with a bit of fixup. - if (gp_IsArc(e)) - { - int result = gp_ContractEdge(theGraph, e); - - // The edge contraction operation pushes one hidden edge then - // recursively calls this method. This method then pushes K - // hidden edges then an integer indicating where the top of - // stack was before the edges were hidden. That integer - // indicator must be decremented, thereby incrementing the - // number of hidden edges to K+1. - // After pushing the K hidden edges and the stackBottom of - // the hidden edges, the recursive call to this method pushes - // six more integers to indicate edges that were moved from - // v to u, so the "hidden edges stackBottom" is in the next - // position down. - int hiddenEdgesStackBottomIndex = sp_GetCurrentSize(theGraph->theStack)-7; - int hiddenEdgesStackBottomValue = sp_Get(theGraph->theStack, hiddenEdgesStackBottomIndex); - - sp_Set(theGraph->theStack, hiddenEdgesStackBottomIndex, hiddenEdgesStackBottomValue - 1); - - return result; - } - - // Now, u and v are not adjacent. Before we do any edge hiding or - // moving, we record the current stack size, as this is the - // stackBottom for the edges that will be hidden next. - hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack); - - // Mark as visited all neighbors of u - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - return NOTOK; - - gp_SetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); - e = gp_GetNextArc(theGraph, e); - } - - // For each edge record of v, if the neighbor is visited, then - // push and hide the edge. - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - { - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - } - e = gp_GetNextArc(theGraph, e); - } - - // Mark as unvisited all neighbors of u - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); - e = gp_GetNextArc(theGraph, e); - } - - // Push the hiddenEdgeStackBottom as a record of how many hidden - // edges were pushed (also, see above for Contract Edge adjustment) - sp_Push(theGraph->theStack, hiddenEdgeStackBottom); - - // Moving v's adjacency list to u is aided by knowing the predecessor - // of u's eBefore (the edge record in u's list before which the - // edge records of v will be added). - eBeforePred = gp_IsArc(eBefore) - ? gp_GetPrevArc(theGraph, eBefore) - : gp_GetLastArc(theGraph, u); - - // Turns out we only need to record six integers related to the edges - // being moved in order to easily restore them later. - sp_Push(theGraph->theStack, eBefore); - sp_Push(theGraph->theStack, gp_GetLastArc(theGraph, v)); - sp_Push(theGraph->theStack, gp_GetFirstArc(theGraph, v)); - sp_Push(theGraph->theStack, eBeforePred); - sp_Push(theGraph->theStack, u); - sp_Push(theGraph->theStack, v); - - // For the remaining edge records of v, reassign the 'v' member - // of each twin arc to indicate u rather than v. - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), u); - e = gp_GetNextArc(theGraph, e); - } - - // If v has any edges left after hiding edges, indicating common neighbors with u, ... - if (gp_IsArc(gp_GetFirstArc(theGraph, v))) - { - // Then perform the list union of v into u between eBeforePred and eBefore - if (gp_IsArc(eBeforePred)) - { - if (gp_IsArc(gp_GetFirstArc(theGraph, v))) - { - gp_SetNextArc(theGraph, eBeforePred, gp_GetFirstArc(theGraph, v)); - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), eBeforePred); - } - } - else - { - gp_SetFirstArc(theGraph, u, gp_GetFirstArc(theGraph, v)); - } - - if (gp_IsArc(eBefore)) - { - if (gp_IsArc(gp_GetLastArc(theGraph, v))) - { - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), eBefore); - gp_SetPrevArc(theGraph, eBefore, gp_GetLastArc(theGraph, v)); - } - } - else - { - gp_SetLastArc(theGraph, u, gp_GetLastArc(theGraph, v)); - } - - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - } - - return OK; -} - -/******************************************************************** - gp_RestoreVertex() - - This method assumes the built-in graph stack contents are the result - of vertex hide, vertex identify and edge contract operations. - This content consists of segments of integers, each segment - corresponding to the removal of a vertex during an edge contraction - or vertex identification in which a vertex v was merged into a - vertex u. The segment contains two blocks of integers. - The first block contains information about u, v, the edge records - in v's adjacency list that were added to u, and where in u's - adjacency list they were added. The second block of integers - contains a list of edges incident to v that were hidden from the - graph because they were incident to neighbors of v that were also - neighbors of u (so they would have produced duplicate edges had - they been left in v's adjacency list when it was merged with u's - adjacency list). - - This method pops the first block of the segment off the stack and - uses the information to help remove v's adjacency list from u and - restore it into v. Then, the second block is removed from the - stack, and each indicated edge is restored from the hidden state. - - It is anticipated that this method will be overloaded by extension - algorithms to perform some processing as each vertex is restored. - Before restoration, the topmost segment has the following structure: - - ... FHE ... LHE HESB e_u_succ e_v_last e_v_first e_u_pred u v - ^------------| - - FHE = First hidden edge - LHE = Last hidden edge - HESB = Hidden edge stack bottom - e_u_succ, e_u_pred = The edges of u between which the edges of v - were inserted. NIL can appear if the edges of v - were added to the beginning or end of u's list - e_v_first, e_v_last = The first and last edges of v's list, once - the hidden edges were removed - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_RestoreVertex(graphP theGraph) -{ - return theGraph->functions.fpRestoreVertex(theGraph); -} - -int _RestoreVertex(graphP theGraph) -{ -int u, v, e_u_succ, e_u_pred, e_v_first, e_v_last, HESB, e; - - if (sp_GetCurrentSize(theGraph->theStack) < 7) - return NOTOK; - - sp_Pop(theGraph->theStack, v); - sp_Pop(theGraph->theStack, u); - sp_Pop(theGraph->theStack, e_u_pred); - sp_Pop(theGraph->theStack, e_v_first); - sp_Pop(theGraph->theStack, e_v_last); - sp_Pop(theGraph->theStack, e_u_succ); - - // If u is not NIL, then vertex v was identified with u. Otherwise, v was - // simply hidden, so we skip to restoring the hidden edges. - if (gp_IsVertex(u)) - { - // Remove v's adjacency list from u, including accounting for degree 0 case - if (gp_IsArc(e_u_pred)) - { - gp_SetNextArc(theGraph, e_u_pred, e_u_succ); - // If the successor edge exists, link it to the predecessor, - // otherwise the predecessor is the new last arc - if (gp_IsArc(e_u_succ)) - gp_SetPrevArc(theGraph, e_u_succ, e_u_pred); - else - gp_SetLastArc(theGraph, u, e_u_pred); - } - else if (gp_IsArc(e_u_succ)) - { - // The successor arc exists, but not the predecessor, - // so the successor is the new first arc - gp_SetPrevArc(theGraph, e_u_succ, NIL); - gp_SetFirstArc(theGraph, u, e_u_succ); - } - else - { - // Just in case u was degree zero - gp_SetFirstArc(theGraph, u, NIL); - gp_SetLastArc(theGraph, u, NIL); - } - - // Place v's adjacency list into v, including accounting for degree 0 case - gp_SetFirstArc(theGraph, v, e_v_first); - gp_SetLastArc(theGraph, v, e_v_last); - if (gp_IsArc(e_v_first)) - gp_SetPrevArc(theGraph, e_v_first, NIL); - if (gp_IsArc(e_v_last)) - gp_SetPrevArc(theGraph, e_v_last, NIL); - - // For each edge record restored to v's adjacency list, reassign the 'v' member - // of each twin arc to indicate v rather than u. - e = e_v_first; - while (gp_IsArc(e)) - { - gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), v); - e = (e == e_v_last ? NIL : gp_GetNextArc(theGraph, e)); - } - } - - // Restore the hidden edges of v, if any - sp_Pop(theGraph->theStack, HESB); - return _RestoreHiddenEdges(theGraph, HESB); -} - -/******************************************************************** - gp_RestoreVertices() - - This method assumes the built-in graph stack has content consistent - with numerous vertex identification or edge contraction operations. - This method unwinds the stack, moving edges back to their original - vertex owners and restoring hidden edges. - This method is a simple iterator that invokes gp_RestoreVertex() - until the stack is empty, so extension algorithms are more likely - to overload gp_RestoreVertex(). - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_RestoreVertices(graphP theGraph) -{ - while (sp_NonEmpty(theGraph->theStack)) - { - if (gp_RestoreVertex(theGraph) != OK) - return NOTOK; - } - - return OK; -} - -/**************************************************************************** - _ComputeArcType() - This is just a little helper function that automates a sequence of decisions - that has to be made a number of times. - An edge record is being added to the adjacency list of a; it indicates that - b is a neighbor. The edgeType can be either 'tree' (EDGE_TYPE_PARENT or - EDGE_TYPE_CHILD) or 'cycle' (EDGE_TYPE_BACK or EDGE_TYPE_FORWARD). - If a or b is a root copy, we translate to the non-virtual counterpart, - then wedetermine which has the lesser DFI. If a has the lower DFI then the - edge record is a tree edge to a child (EDGE_TYPE_CHILD) if edgeType indicates - a tree edge. If edgeType indicates a cycle edge, then it is a forward cycle - edge (EDGE_TYPE_FORWARD) to a descendant. - Symmetric conditions define the types for a > b. - ****************************************************************************/ - -int _ComputeArcType(graphP theGraph, int a, int b, int edgeType) -{ - a = gp_IsVirtualVertex(theGraph, a) ? gp_GetPrimaryVertexFromRoot(theGraph, a) : a; - b = gp_IsVirtualVertex(theGraph, b) ? gp_GetPrimaryVertexFromRoot(theGraph, b) : b; - - if (a < b) - return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_CHILD : EDGE_TYPE_FORWARD; - - return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_PARENT : EDGE_TYPE_BACK; -} - -/**************************************************************************** - _SetEdgeType() - When we are restoring an edge, we must restore its type (tree edge or cycle edge). - We can deduce what the type was based on other information in the graph. Each - arc of the edge gets the appropriate type setting (parent/child or back/forward). - This method runs in constant time plus the degree of vertex u, or constant - time if u is known to have a degree bound by a constant. - ****************************************************************************/ - -int _SetEdgeType(graphP theGraph, int u, int v) -{ -int e, eTwin, u_orig, v_orig; - - // If u or v is a virtual vertex (a root copy), then get the non-virtual counterpart. - u_orig = gp_IsVirtualVertex(theGraph, u) ? (gp_GetPrimaryVertexFromRoot(theGraph, u)) : u; - v_orig = gp_IsVirtualVertex(theGraph, v) ? (gp_GetPrimaryVertexFromRoot(theGraph, v)) : v; - - // Get the edge for which we will set the type - - e = gp_GetNeighborEdgeRecord(theGraph, u, v); - eTwin = gp_GetTwinArc(theGraph, e); - - // If u_orig is the parent of v_orig, or vice versa, then the edge is a tree edge - - if (gp_GetVertexParent(theGraph, v_orig) == u_orig || - gp_GetVertexParent(theGraph, u_orig) == v_orig) - { - if (u_orig > v_orig) - { - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_PARENT); - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_CHILD); - } - else - { - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_PARENT); - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_CHILD); - } - } - - // Otherwise it is a back edge - - else - { - if (u_orig > v_orig) - { - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_BACK); - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD); - } - else - { - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_BACK); - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_FORWARD); - } - } - - return OK; -} - -/******************************************************************** - _DeleteUnmarkedEdgesInBicomp() - - This function deletes from a given biconnected component all edges - whose visited member is zero. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetEdgeVisited(theGraph, e) ? gp_GetNextArc(theGraph, e) : gp_DeleteEdge(theGraph, e, 0); - } - } - return OK; -} - -/******************************************************************** - _ClearInvertedFlagsInBicomp() - - This function clears the inverted flag markers on any edges in a - given biconnected component. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - gp_ClearEdgeFlagInverted(theGraph, e); - } - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _GetBicompSize() - - Determine the number of vertices in the bicomp. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - Returns a positive number on success, NOTOK on implementation failure - ********************************************************************/ - -int _GetBicompSize(graphP theGraph, int BicompRoot) -{ -int V, e; -int theSize = 0; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - theSize++; - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return theSize; -} - -/******************************************************************** - debugNOTOK() - This function provides a non-void wrapper for exit(). - This is useful for debugging as it allows compilation of an exit - command in places where NOTOK is returned. - In exhaustive testing, we want to bail on the first NOTOK that occurs. - Comment out the exit() call to get a stack trace. - ********************************************************************/ - -int debugNOTOK() -{ - //exit(-1); - return 0; // NOTOK is normally defined to be zero -} diff --git a/planarity_src/c/listcoll.h b/planarity_src/c/listcoll.h deleted file mode 100644 index 92195ba..0000000 --- a/planarity_src/c/listcoll.h +++ /dev/null @@ -1,157 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#ifndef _LISTCOLL_H -#define _LISTCOLL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* This include is needed for memset and memcpy */ -#include - -typedef struct -{ - int prev, next; -} lcnode; - -typedef struct -{ - int N; - lcnode *List; -} listCollectionRec; - -typedef listCollectionRec * listCollectionP; - -listCollectionP LCNew(int N); -void LCFree(listCollectionP *pListColl); - -void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode); -void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode); - -#ifndef SPEED_MACROS - -void LCReset(listCollectionP listColl); -void LCCopy(listCollectionP dst, listCollectionP src); - -int LCGetNext(listCollectionP listColl, int theList, int theNode); -int LCGetPrev(listCollectionP listColl, int theList, int theNode); - -int LCPrepend(listCollectionP listColl, int theList, int theNode); -int LCAppend(listCollectionP listColl, int theList, int theNode); -int LCDelete(listCollectionP listColl, int theList, int theNode); - -#else - -/* void LCReset(listCollectionP listColl); */ - -#define LCReset(listColl) memset(listColl->List, NIL_CHAR, listColl->N*sizeof(lcnode)) - -/* void LCCopy(listCollectionP dst, listCollectionP src) */ - -#define LCCopy(dst, src) memcpy(dst->List, src->List, src->N*sizeof(lcnode)) - -/* int LCGetNext(listCollectionP listColl, int theList, int theNode); - Return theNode's successor, unless it is theList head pointer */ - -#define LCGetNext(listColl, theList, theNode) listColl->List[theNode].next==theList ? NIL : listColl->List[theNode].next - -/* int LCGetPrev(listCollectionP listColl, int theList, int theNode); - Return theNode's predecessor unless theNode is theList head. - To start going backwards, use NIL for theNode, which returns theList head's predecessor - Usage: Obtain last node, loop while NIL not returned, process node then get predecessor. - After theList head processed, get predecessor returns NIL because we started with - theList head's predecessor. */ - -#define LCGetPrev(listColl, theList, theNode) \ - (theNode==NIL \ - ? listColl->List[theList].prev \ - : theNode==theList ? NIL : listColl->List[theNode].prev) - -/* int LCPrepend(listCollectionP listColl, int theList, int theNode); - After an append, theNode is last, which in a circular list is the direct predecessor - of the list head node, so we just back up one. For singletons, this has no effect.*/ - -#define LCPrepend(listColl, theList, theNode) listColl->List[LCAppend(listColl, theList, theNode)].prev - -/* int LCAppend(listCollectionP listColl, int theList, int theNode); - If theList is empty, then theNode becomes its only member and is returned. - Otherwise, theNode is placed before theList head, which is returned. */ - -#define LCAppend(listColl, theList, theNode) \ - (theList==NIL \ - ? (listColl->List[theNode].prev = listColl->List[theNode].next = theNode) \ - : (listColl->List[theNode].next = theList, \ - listColl->List[theNode].prev = listColl->List[theList].prev, \ - listColl->List[listColl->List[theNode].prev].next = theNode, \ - listColl->List[theList].prev = theNode, \ - theList)) - -/* int LCDelete(listCollectionP listColl, int theList, int theNode); - If theList contains only one node, then NIL it out and return NIL meaning empty list - Otherwise, join the predecessor and successor, then - return either the list head or its successor if the deleted node is the list head - (in that case, the caller makes the successor become the new list head).*/ - - -#define LCDelete(listColl, theList, theNode) \ - listColl->List[theList].next == theList \ - ? (listColl->List[theList].prev = listColl->List[theList].next = NIL) \ - : (listColl->List[listColl->List[theNode].prev].next = listColl->List[theNode].next, \ - listColl->List[listColl->List[theNode].next].prev = listColl->List[theNode].prev, \ - (theList==theNode ? listColl->List[theNode].next : theList)) - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/planarity_src/c/nauty/makeg.c b/planarity_src/c/nauty/makeg.c deleted file mode 100644 index 97d085f..0000000 --- a/planarity_src/c/nauty/makeg.c +++ /dev/null @@ -1,1731 +0,0 @@ -/* makeg.c : Find all graphs with a given number of vertices, - a given range of numbers of edges, and a given bound - on the maximum degree. - - The output is to stdout in either y format or nauty format. - The output graphs are optionally canonically labelled. - - Usage: makeg [-c -t -b -n -u -v -l] [-d] n [mine [maxe [mod res]]] - - n = the number of vertices (1..16) - mine = the minimum number of edges (no bounds if missing) - maxe = the maximum number of edges (same as mine if missing) - mod, res = a way to restrict the output to a subset. - All the graphs in G(n,mine..maxe) are divided into - disjoint classes C(mod,0),C(mod,1),...,C(mod,mod-1), - of very approximately equal size. - Only the class C(mod,res) is written. - The usual relationships between modulo classes are - obeyed; for example C(4,3) = C(8,3) union C(8,7). - -c : only write connected graphs - -t : only generate triangle-free graphs - -b : only generate bipartite graphs - -d : specify an upper bound for the maximum degree. - The value of the upper bound must be adjacent to - the "d". Example: -d6 - -n : use nauty format instead of y format for output - -u : do not output any graphs, just generate and count them - -v : display counts by number of edges to stderr - -l : canonically label output graphs - -Output formats. - - If -n is absent, any output graphs are written in y format. - - Each graph occupies one line with a terminating newline. - Except for the newline, each byte has the format 01xxxxxx, where - each "x" represents one bit of data. - First byte: xxxxxx is the number of vertices n - Other ceiling(n(n-1)/12) bytes: These contain the upper triangle of - the adjacency matrix in column major order. That is, the entries - appear in the order (0,1),(0,2),(1,2),(0,3),(1,3),(2,3),(0,4),... . - The bits are used in left to right order within each byte. - Any unused bits on the end are set to zero. - - If -n is present, any output graphs are written in nauty format. - - For a graph of n vertices, the output consists of n+1 long ints - (even if a setword is shorter than a long int). The first contains - n, and the others contain the adjacency matrix. Long int i of - the adjacency matrix (0 <= i <= n-1) is the set of neighbours of - vertex i, cast from setword to long int. - -OUTPROC feature. - - By defining the C preprocessor variable OUTPROC at compile time - (for Unix the syntax is -DOUTPROC=procname on the cc command), - makeg can be made to call a procedure of your manufacture with each - output graph instead of writing anything. Your procedure needs to - have type void and the argument list (FILE *f, graph *g, int n). - f is a stream open for writing (in fact, in the current version it - is always stdout), g is the graph in nauty format, and n is the number - of vertices. Your procedure can be in a separate file so long as it - is linked with makeg. The global variables nooutput, nautyformat - and canonise (all type boolean) can be used to test for the presence - of the flags -u, -n and -l, respectively. - -INSTRUMENT feature. - - If the C preprocessor variable INSTRUMENT is defined at compile time, - extra code is inserted to collect statistics during execution, and - more information is written to stderr at termination. - -************************************************************************** - -Sample performance statistics. - - Here we give some graph counts and execution times on a Sun - Sparcstation ELC (nominally 24 mips). makeg version of 11/3/92. - - - General Graphs (to n(n-1)/4 edges) makeg -u 0 - 1 1 m = floor(n(n-1)/4) - 2 1 - 3 2 - 4 7 - 5 20 - 6 78 0.02 sec - 7 522 0.11 sec - 8 6996 1.22 sec - 9 154354 22.4 sec - 10 6002584 13.6 min - 11 509498932 18.9 hr - 12 90548672803 3293 hr (version of 5/6/93) - - Triangle-Free Graphs makeg -u -t - 1 1 - 2 2 - 3 3 - 4 7 - 5 14 - 6 38 - 7 107 0.06 sec - 8 410 0.28 sec - 9 1897 1.32 sec - 10 12172 9.3 sec - 11 105071 80.5 sec - 12 1262180 16.1 min - 13 20797002 4.80 hr - 14 467871369 120 hr (est) - - Bipartite Graphs makeg -u -b - 1 1 - 2 2 - 3 3 - 4 7 - 5 13 - 6 35 - 7 88 0.05 sec - 8 303 0.24 sec - 9 1119 1.0 sec - 10 5479 6.0 sec - 11 32303 40.7 sec - 12 251135 356 sec - 13 2527712 74 min - 14 33985853 20.1 hr - 15 611846940 430 hr (est) - -************************************************************************** - - Author: B. D. McKay, Sep 1991. - Copyright B. McKay (1991, 1992). All rights reserved. - This software is subject to the conditions and waivers - detailed in the file nauty.h. - - Changes: Nov 18, 1991 : added -d switch - fixed operation for n=16 - Nov 26, 1991 : added OUTPROC feature - Nov 29, 1991 : -c implies mine >= n-1 - Jan 8, 1992 : make writeny() not static - Jan 10, 1992 : added -n switch - Feb 9, 1992 : fixed case of n=1 - Feb 16, 1992 : changed mine,maxe,maxdeg testing - Feb 19, 1992 : added -b, -t and -u options - documented OUTPROC and added external - declaration for it. - Feb 20, 1992 : added -v option - Feb 22, 1992 : added INSTRUMENT compile-time option - Feb 23, 1992 : added xbnds() for more effective pruning - Feb 24, 1992 : added -l option - Feb 25, 1992 : changed writenauty() to use fwrite() - Mar 11, 1992 : completely revised many parts, incl - new refinement procedure for fast rejection, - distance invariant for regular graphs - May 19, 1992 : modified userautomproc slightly. xorb[] - is no longer idempotent but it doesn't matter. - Speed-up of 2-5% achieved. - June 5, 1993 : removed ";" after "CPUDEFS" to avoid illegal - empty declaration. - November 24, 1994 : tested for 0 <= res < mod - - -**************************************************************************/ - -#define MAXN 16 /* not more than 16 */ -#include "naututil.h" /* which includes nauty.h and stdio.h */ - -CPUDEFS - -static void (*outproc)(); -#ifdef OUTPROC -extern void OUTPROC(); -#endif - -static FILE *outfile; /* file for output graphs */ -static FILE *msgfile; /* file for messages */ -static boolean connec; /* presence of -c */ -static boolean bipartite; /* presence of -b */ -static boolean trianglefree; /* presence of -b or -t */ -static boolean verbose; /* presence of -v */ -boolean nautyformat; /* presence of -n */ -boolean nooutput; /* presence of -u */ -boolean canonise; /* presence of -l */ -static int maxdeg,maxn,mine,maxe,nprune,mod,res,curres; -// CHANGE start -int g_maxn, g_mine, g_maxe, g_mod, g_res; -char g_command; -FILE *g_msgfile; -// CHANGE end -static graph gcan[MAXN]; - -static int xbit[] = {0x0001,0x0002,0x0004,0x0008, - 0x0010,0x0020,0x0040,0x0080, - 0x0100,0x0200,0x0400,0x0800, - 0x1000,0x2000,0x4000,0x8000}; - -#define XFIRSTBIT(x) \ - ((x)&0xFF ? 7-leftbit[(x)&0xFF] : 15-leftbit[((x)>>8)&0xFF]) -#define XPOPCOUNT(x) (bytecount[((x)>>8)&0xFF] + bytecount[(x)&0xFF]) - -typedef struct -{ - int ne,dmax; /* values used for xlb,xub calculation */ - int xlb,xub; /* saved bounds on extension degree */ - int lo,hi; /* work purposes for orbit calculation */ - int xstart[MAXN+1]; /* index into xset[] for each cardinality */ - int *xset; /* array of all x-sets in card order */ - int *xcard; /* cardinalities of all x-sets */ - int *xinv; /* map from x-set to index in xset */ - int *xorb; /* min orbit representative */ - int *xx; /* (-b or -t) all but largest legal x-set */ -} leveldata; - -static leveldata data[MAXN]; /* data[n] is data for n -> n+1 */ -static long count[1+MAXN*(MAXN-1)/2]; /* counts by number of edges */ - -#ifdef INSTRUMENT -static long nodes[MAXN],rigidnodes[MAXN],fertilenodes[MAXN]; -static long a1calls,a1nauty,a1succs; -static long a2calls,a2nauty,a2uniq,a2succs; -#endif - -/************************************************************************/ - -void -writeny(f,g,n) /* write graph g (n vertices) to file f in y format */ -FILE *f; -graph *g; -int n; -{ - static char ybit[] = {32,16,8,4,2,1}; - char s[(MAXN*(MAXN-1)/2 + 5)/6 + 4]; - register int i,j,k; - register char y,*sp; - - sp = s; - *(sp++) = 0x40 | n; - y = 0x40; - - k = -1; - for (j = 1; j < n; ++j) - for (i = 0; i < j; ++i) - { - if (++k == 6) - { - *(sp++) = y; - y = 0x40; - k = 0; - } - if (g[i] & bit[j]) y |= ybit[k]; - } - if (n >= 2) *(sp++) = y; - *(sp++) = '\n'; - *sp = '\0'; - - if (fputs(s,f) == EOF || ferror(f)) - { - fprintf(stderr,">E writeny : error on writing file\n"); - exit(2); - } -} - -/***********************************************************************/ -// CHANGE start (comment out this function; changed comment style within) -/* -static void -nullwrite(f,g,n) // don't write graph g (n vertices) to file f -FILE *f; -graph *g; -int n; -{ -} -*/ -// CHANGE end -/***********************************************************************/ - -void -writenauty(f,g,n) /* write graph g (n vertices) to file f in nauty format */ -FILE *f; -graph *g; -int n; -{ - long buffer[MAXN+1]; - register int i; - - buffer[0] = n; - for (i = 0; i < n; ++i) - buffer[i+1] = g[i]; - - if (fwrite((char*)buffer,sizeof(long),n+1,f) != n+1) - { - fprintf(stderr,">E writenauty : error on writing file\n"); - exit(2); - } -} - -/*********************************************************************/ - -static boolean -isconnected(g,n) /* test if g is connected */ -graph *g; -int n; -{ - register setword seen,expanded,toexpand; - register int i; - - seen = bit[0]; - expanded = 0; - - // CHANGE added extra parens around assignment to eliminate compile warning - while ((toexpand = (seen & ~expanded))) /* not == */ - { - i = FIRSTBIT(toexpand); - expanded |= bit[i]; - seen |= g[i]; - } - - return POPCOUNT(seen) == n; -} - -/**************************************************************************/ - -static boolean -distinvar(g,invar,n) /* make distance invariant */ -graph *g; /* exit immediately FALSE if n-1 not maximal */ -int *invar,n; /* else exit TRUE */ -{ - register int w; - register setword workset,frontier; - setword sofar; - int inv,d,v; - - for (v = n-1; v >= 0; --v) - { - inv = 0; - sofar = frontier = bit[v]; - for (d = 1; frontier != 0; ++d) - { - workset = 0; - inv += POPCOUNT(frontier) ^ (0x57 + d); - while (frontier) - { - w = FIRSTBIT(frontier); - frontier &= ~bit[w]; - workset |= g[w]; - } - frontier = workset & ~sofar; - sofar |= frontier; - } - invar[v] = inv; - if (v < n-1 && inv > invar[n-1]) return FALSE; - } - return TRUE; -} - -/**************************************************************************/ - -static void -makexgraph(g,h,n) /* make x-format graph from nauty format graph */ -graph *g; -int *h,n; -{ - register setword gi; - register int i,j,hi; - - for (i = 0; i < n; ++i) - { - hi = 0; - gi = g[i]; - while (gi) - { - j = FIRSTBIT(gi); - gi &= ~bit[j]; - hi |= xbit[j]; - } - h[i] = hi; - } -} - -/**************************************************************************/ - -static void -makebgraph(g,h,n) /* make x-format graph of different colour graph */ -graph *g; -int *h,n; -{ - register setword seen1,seen2,expanded,w; - setword restv; - register int i,xseen1,xseen2; - - restv = 0; - for (i = 0; i < n; ++i) - restv |= bit[i]; - - seen1 = seen2 = 0; - expanded = 0; - - while (TRUE) - { - if ((w = ((seen1 | seen2) & ~expanded)) == 0) - { - xseen1 = 0; - w = seen1; - while (w) - { - i = FIRSTBIT(w); - w &= ~bit[i]; - xseen1 |= xbit[i]; - } - xseen2 = 0; - w = seen2; - while (w) - { - i = FIRSTBIT(w); - w &= ~bit[i]; - xseen2 |= xbit[i]; - } - - w = seen1; - while (w) - { - i = FIRSTBIT(w); - w &= ~bit[i]; - h[i] = xseen2; - } - w = seen2; - while (w) - { - i = FIRSTBIT(w); - w &= ~bit[i]; - h[i] = xseen1; - } - - restv &= ~(seen1 | seen2); - if (restv == 0) return; - i = FIRSTBIT(restv); - seen1 = bit[i]; - seen2 = 0; - } - else - i = FIRSTBIT(w); - - expanded |= bit[i]; - if (bit[i] & seen1) seen2 |= g[i]; - else seen1 |= g[i]; - } -} - -/**************************************************************************/ - -static void -makeleveldata() /* make the level data for each level */ -{ - register int i,j,h; - int n,nn,nxsets,tttn; - long ncj; - leveldata *d; - int *xset,*xcard,*xinv; - int xw,cw; -// extern char *malloc(); - - for (n = 1; n < maxn; ++n) - { - nn = maxdeg <= n ? maxdeg : n; - ncj = nxsets = 1; - for (j = 1; j <= nn; ++j) - { - ncj = (ncj * (n - j + 1)) / j; - nxsets += ncj; - } - tttn = 1 << n; - - d = &data[n]; - - d->xset = xset = (int*) malloc(nxsets * sizeof(int)); - d->xcard = xcard = (int*) malloc(nxsets * sizeof(int)); - d->xinv = xinv = (int*) malloc(tttn * sizeof(int)); - d->xorb = (int*) malloc(nxsets * sizeof(int)); - d->xx = d->xcard; - - if (xset==NULL || xcard==NULL || xinv==NULL || d->xorb==NULL) - { - fprintf(stderr,">E makeg: malloc failed in makeleveldata()\n"); - exit(2); - } - - d->ne = d->dmax = d->xlb = d->xub = -1; - - j = 0; - for (i = 0; i < tttn; ++i) - if ((h = XPOPCOUNT(i)) <= maxdeg) - { - xset[j] = i; - xcard[j] = h; - ++j; - } - - if (j != nxsets) - { - fprintf(stderr,">E makeg: j=%d mxsets=%d\n",j,nxsets); - exit(2); - } - - h = 1; - do - h = 3 * h + 1; - while (h < nxsets); - - do - { - for (i = h; i < nxsets; ++i) - { - xw = xset[i]; - cw = xcard[i]; - for (j = i; xcard[j-h] > cw || - // CHANGE added parens around last && to eliminate compile warning - (xcard[j-h] == cw && xset[j-h] > xw); ) - { - xset[j] = xset[j-h]; - xcard[j] = xcard[j-h]; - if ((j -= h) < h) break; - } - xset[j] = xw; - xcard[j] = cw; - } - h /= 3; - } - while (h > 0); - - for (i = 0; i < nxsets; ++i) - xinv[xset[i]] = i; - - d->xstart[0] = 0; - for (i = 1; i < nxsets; ++i) - if (xcard[i] > xcard[i-1]) d->xstart[xcard[i]] = i; - d->xstart[xcard[nxsets-1]+1] = nxsets; - } -} - -/**************************************************************************/ - -static UPROC -userautomproc(count,p,orbits,numorbits,stabvertex,n) -int count,numorbits,stabvertex,n; /* form orbits on powerset of VG */ -permutation *p; /* called by nauty */ -nvector *orbits; /* operates on data[n] */ -{ - register int i,j1,j2; - register int moved,pxi,pi; - int w,lo,hi; - int *xorb,*xset,*xinv; - - xorb = data[n].xorb; - xset = data[n].xset; - xinv = data[n].xinv; - lo = data[n].lo; - hi = data[n].hi; - - if (count == 1) /* first automorphism */ - for (i = lo; i < hi; ++i) - xorb[i] = i; - - moved = 0; - for (i = 0; i < n; ++i) - if (p[i] != i) moved |= xbit[i]; - - for (i = lo; i < hi; ++i) - { - if ((w = xset[i] & moved) == 0) continue; - pxi = xset[i] & ~moved; - while (w) - { - j1 = XFIRSTBIT(w); - w &= ~xbit[j1]; - pxi |= xbit[p[j1]]; - } - pi = xinv[pxi]; - - j1 = xorb[i]; - while (xorb[j1] != j1) - j1 = xorb[j1]; - j2 = xorb[pi]; - while (xorb[j2] != j2) - j2 = xorb[j2]; - - if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1; - else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2; - } -} - -/***************************************************************************** -* * -* refinex(g,lab,ptn,level,numcells,count,active,goodret,code,m,n) is a * -* custom version of refine() which can exit quickly if required. * -* * -* Only use at level==0. * -* goodret : whether to do an early return for code 1 * -* code := -1 for n-1 not max, 0 for maybe, 1 for definite * -* * -*****************************************************************************/ - -static void -refinex(g,lab,ptn,level,numcells,count,active,goodret,code,m,n) -graph *g; -register nvector *lab,*ptn; -permutation *count; -int *numcells,level,m,n,*code; -set *active; -boolean goodret; -{ - register int i,c1,c2,labc1; - register setword x; - int split1,split2,cell1,cell2; - int cnt,bmin,bmax; - set *gptr; - setword workset; - int workperm[MAXN]; - int bucket[MAXN+2]; - - if (n == 1) - { - *code = 1; - return; - } - - *code = 0; - split1 = -1; - while (*numcells < n && ((split1 = nextelement(active,1,split1)) >= 0 - || (split1 = nextelement(active,1,-1)) >= 0)) - { - DELELEM1(active,split1); - for (split2 = split1; ptn[split2] > 0; ++split2) - {} - if (split1 == split2) /* trivial splitting cell */ - { - gptr = GRAPHROW(g,lab[split1],1); - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - for (cell2 = cell1; ptn[cell2] > 0; ++cell2) - {} - if (cell1 == cell2) - continue; - c1 = cell1; - c2 = cell2; - while (c1 <= c2) - { - labc1 = lab[c1]; - if (ISELEM1(gptr,labc1)) - ++c1; - else - { - lab[c1] = lab[c2]; - lab[c2] = labc1; - --c2; - } - } - if (c2 >= cell1 && c1 <= cell2) - { - ptn[c2] = 0; - ++*numcells; - ADDELEM1(active,c1); - } - } - } - - else /* nontrivial splitting cell */ - { - workset = 0; - for (i = split1; i <= split2; ++i) - workset |= bit[lab[i]]; - - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - for (cell2 = cell1; ptn[cell2] > 0; ++cell2) - {} - if (cell1 == cell2) - continue; - i = cell1; - // CHANGE added extra parens around assignment to eliminate compile warning - if ((x = workset & g[lab[i]])) /* not == */ - cnt = POPCOUNT(x); - else - cnt = 0; - count[i] = bmin = bmax = cnt; - bucket[cnt] = 1; - while (++i <= cell2) - { - // CHANGE added extra parens around assignment to eliminate compile warning - if ((x = workset & g[lab[i]])) /* not == */ - cnt = POPCOUNT(x); - else - cnt = 0; - while (bmin > cnt) - bucket[--bmin] = 0; - while (bmax < cnt) - bucket[++bmax] = 0; - ++bucket[cnt]; - count[i] = cnt; - } - if (bmin == bmax) - { - continue; - } - c1 = cell1; - for (i = bmin; i <= bmax; ++i) - if (bucket[i]) - { - c2 = c1 + bucket[i]; - bucket[i] = c1; - if (c1 != cell1) - { - ADDELEM1(active,c1); - ++*numcells; - } - if (c2 <= cell2) - ptn[c2-1] = 0; - c1 = c2; - } - for (i = cell1; i <= cell2; ++i) - workperm[bucket[count[i]]++] = lab[i]; - for (i = cell1; i <= cell2; ++i) - lab[i] = workperm[i]; - } - } - - if (ptn[n-2] == 0) - { - if (lab[n-1] == n-1) - { - *code = 1; - if (goodret) return; - } - else - { - *code = -1; - return; - } - } - else - { - i = n - 1; - while (1) - { - if (lab[i] == n-1) break; - --i; - if (ptn[i] == 0) - { - *code = -1; - return; - } - } - } - } -} - -/**************************************************************************/ - -static void -makecanon(g,gcan,n) -graph *g,*gcan; /* gcan := canonise(g) */ -int n; -{ - nvector lab[MAXN],ptn[MAXN],orbits[MAXN]; - statsblk stats; - static DEFAULTOPTIONS(options); - setword workspace[50]; - - options.writemarkers = FALSE; - options.writeautoms = FALSE; - options.getcanon = TRUE; - - nauty(g,lab,ptn,NILSET,orbits,&options,&stats,workspace,50,1,n,gcan); -} - -/**************************************************************************/ - -static boolean -accept1(g,n,x,gx,deg,rigid) /* decide if n in theta(g+x) */ -graph *g,*gx; /* version for n+1 < maxn */ -int n,*deg; -int x; -boolean *rigid; -{ - register int i; - nvector lab[MAXN],ptn[MAXN],orbits[MAXN]; - permutation count[MAXN]; - graph h[MAXN]; - int xw; - int nx,numcells,code; - int i0,i1,degn; - set active[MAXM]; - statsblk stats; - static DEFAULTOPTIONS(options); - setword workspace[50]; - -#ifdef INSTRUMENT - ++a1calls; -#endif - - nx = n + 1; - for (i = 0; i < n; ++i) - gx[i] = g[i]; - gx[n] = 0; - deg[n] = degn = XPOPCOUNT(x); - - xw = x; - while (xw) - { - i = XFIRSTBIT(xw); - xw &= ~xbit[i]; - gx[i] |= bit[n]; - gx[n] |= bit[i]; - ++deg[i]; - } - - i0 = 0; - i1 = n; - for (i = 0; i < nx; ++i) - { - if (deg[i] == degn) lab[i1--] = i; - else lab[i0++] = i; - ptn[i] = 1; - } - ptn[n] = 0; - if (i0 == 0) - { - numcells = 1; - active[0] = bit[0]; - } - else - { - numcells = 2; - active[0] = bit[0] | bit[i1+1]; - ptn[i1] = 0; - } - refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx); - - if (code < 0) return FALSE; - - if (numcells == nx) - { - *rigid = TRUE; -#ifdef INSTRUMENT - ++a1succs; -#endif - return TRUE; - } - - options.writemarkers = FALSE; - options.writeautoms = FALSE; - options.getcanon = TRUE; - options.defaultptn = FALSE; - options.userautomproc = userautomproc; - - active[0] = 0; -#ifdef INSTRUMENT - ++a1nauty; -#endif - nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,h); - - if (orbits[lab[n]] == orbits[n]) - { - *rigid = stats.numorbits == nx; -#ifdef INSTRUMENT - ++a1succs; -#endif - return TRUE; - } - else - return FALSE; -} - -/**************************************************************************/ - -static boolean -accept2(g,n,x,gx,deg,nuniq) /* decide if n in theta(g+x) */ -graph *g,*gx; /* version for n+1 == maxn */ -int n,x,deg[]; -boolean nuniq; -{ - register int i; - nvector lab[MAXN],ptn[MAXN],orbits[MAXN]; - int degx[MAXN],invar[MAXN]; - setword vmax,gv; - int qn,qv; - permutation count[MAXN]; - int xw; - int nx,numcells,code; - int degn,i0,i1,j,j0,j1; - set active[MAXM]; - statsblk stats; - static DEFAULTOPTIONS(options); - setword workspace[50]; - -#ifdef INSTRUMENT - ++a2calls; - if (nuniq) ++a2uniq; -#endif - nx = n + 1; - for (i = 0; i < n; ++i) - { - gx[i] = g[i]; - degx[i] = deg[i]; - } - gx[n] = 0; - degx[n] = degn = XPOPCOUNT(x); - - xw = x; - while (xw) - { - i = XFIRSTBIT(xw); - xw &= ~xbit[i]; - gx[i] |= bit[n]; - gx[n] |= bit[i]; - ++degx[i]; - } - - if (nuniq) - { -#ifdef INSTRUMENT - ++a2succs; -#endif - if (canonise) makecanon(gx,gcan,nx); - return TRUE; - } - - i0 = 0; - i1 = n; - for (i = 0; i < nx; ++i) - { - if (degx[i] == degn) lab[i1--] = i; - else lab[i0++] = i; - ptn[i] = 1; - } - ptn[n] = 0; - if (i0 == 0) - { - numcells = 1; - active[0] = bit[0]; - - if (!distinvar(gx,invar,nx)) return FALSE; - qn = invar[n]; - j0 = 0; - j1 = n; - while (j0 <= j1) - { - j = lab[j0]; - qv = invar[j]; - if (qv < qn) - ++j0; - else - { - lab[j0] = lab[j1]; - lab[j1] = j; - --j1; - } - } - if (j0 > 0) - { - if (j0 == n) - { -#ifdef INSTRUMENT - ++a2succs; -#endif - if (canonise) makecanon(gx,gcan,nx); - return TRUE; - } - ptn[j1] = 0; - ++numcells; - active[0] |= bit[j0]; - } - } - else - { - numcells = 2; - ptn[i1] = 0; - active[0] = bit[0] | bit[i1+1]; - - vmax = 0; - for (i = i1+1; i < nx; ++i) - vmax |= bit[lab[i]]; - - gv = gx[n] & vmax; - qn = POPCOUNT(gv); - - j0 = i1+1; - j1 = n; - while (j0 <= j1) - { - j = lab[j0]; - gv = gx[j] & vmax; - qv = POPCOUNT(gv); - if (qv > qn) - return FALSE; - else if (qv < qn) - ++j0; - else - { - lab[j0] = lab[j1]; - lab[j1] = j; - --j1; - } - } - if (j0 > i1+1) - { - if (j0 == n) - { -#ifdef INSTRUMENT - ++a2succs; -#endif - if (canonise) makecanon(gx,gcan,nx); - return TRUE; - } - ptn[j1] = 0; - ++numcells; - active[0] |= bit[j0]; - } - } - - refinex(gx,lab,ptn,0,&numcells,count,active,TRUE,&code,1,nx); - - if (code < 0) return FALSE; - else if (code > 0 || numcells >= nx-4) - { -#ifdef INSTRUMENT - ++a2succs; -#endif - if (canonise) makecanon(gx,gcan,nx); - return TRUE; - } - - options.writemarkers = FALSE; - options.writeautoms = FALSE; - options.getcanon = TRUE; - options.defaultptn = FALSE; - - active[0] = 0; -#ifdef INSTRUMENT - ++a2nauty; -#endif - nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gcan); - - if (orbits[lab[n]] == orbits[n]) - { -#ifdef INSTRUMENT - ++a2succs; -#endif - if (canonise) makecanon(gx,gcan,nx); - return TRUE; - } - else - return FALSE; -} - -/**************************************************************************/ - -static void -xbnds(n,ne,dmax) /* find bounds on extension degree */ -int n,ne,dmax; /* store answer in data[*].* */ -{ - register int xlb,xub,d,nn,m,xc; - - xlb = n == 1 ? 0 : (dmax > (2*ne + n - 2)/(n - 1) ? - dmax : (2*ne + n - 2)/(n - 1)); - xub = n < maxdeg ? n : maxdeg; - - for (xc = xub; xc >= xlb; --xc) - { - d = xc; - m = ne + d; - for (nn = n+1; nn < maxn; ++nn) - { - if (d < (2*m + nn - 2)/(nn - 1)) d = (2*m + nn - 2)/(nn - 1); - m += d; - } - if (d > maxdeg || m > maxe) xub = xc - 1; - else break; - } - - if (ne + xlb < mine) - for (xc = xlb; xc <= xub; ++xc) - { - m = ne + xc; - for (nn = n + 1; nn < maxn; ++nn) - m += maxdeg < nn ? maxdeg : nn; - if (m < mine) xlb = xc + 1; - else break; - } - - data[n].ne = ne; - data[n].dmax = dmax; - data[n].xlb = xlb; - data[n].xub = xub; -} - -/**************************************************************************/ - -static void -tfextend(g,n,deg,ne,rigid,xlb,xub) /* extend from n to n+1 */ -graph *g; /* version for triangle-free graphs */ -int n,*deg,ne,xlb,xub; -boolean rigid; -{ - register int x,d,bitv,hv; - int *xinv,*xorb,xc; - int nx,i,j,dmax,xlbx,xubx; - graph gx[MAXN]; - int h[MAXN],*xx,v,ixx,nchild; - int degx[MAXN]; - boolean rigidx; - -#ifdef INSTRUMENT - boolean haschild; - - haschild = FALSE; - ++nodes[n]; - if (rigid) ++rigidnodes[n]; -#endif - - nx = n + 1; - dmax = deg[n-1]; - d = xbit[n-1]; - for (i = 0; i < n-1; ++i) - if (deg[i] == dmax) d |= xbit[i]; - - if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb; - if (xlb > xub) return; - - xinv = data[n].xinv; - xorb = data[n].xorb; - xx = data[n].xx; - - makexgraph(g,h,n); - xx[0] = 0; - nchild = 1; - - if (nx == maxn) - { - if (xlb == 0 && !connec && (nx != nprune - || (curres = curres == 0 ? mod-1 : curres-1) == 0) - && accept2(g,n,0,gx,deg,FALSE)) - { - ++count[ne]; -#ifdef INSTRUMENT - haschild = TRUE; -#endif - (*outproc)(outfile,canonise ? gcan : gx,nx); - } - - if (xub > 0) - for (v = 0; v < n; ++v) - { - bitv = xbit[v]; - hv = h[v]; - - for (ixx = nchild; --ixx >= 0;) - if ((hv & xx[ixx]) == 0) - { - x = xx[ixx] | bitv; - xc = XPOPCOUNT(x); - if (xc < xub) xx[nchild++] = x; - - if (xc >= xlb && (rigid || xorb[xinv[x]] == xinv[x]) - // CHANGE added parens around last && to eliminate compile warning - && (xc > dmax || (xc == dmax && (x & d) == 0))) - { - if ((nx != nprune - || (curres = curres == 0 ? mod-1 : curres-1) == 0) - && accept2(g,n,x,gx,deg, - // CHANGE added parens around last && to eliminate compile warning - xc > dmax+1 || (xc == dmax+1 && (x & d) == 0)) - && (!connec || isconnected(gx,nx))) - { - ++count[ne+xc]; -#ifdef INSTRUMENT - haschild = TRUE; -#endif - (*outproc)(outfile,canonise ? gcan : gx,nx); - } - } - } - } - } - else - { - if (xlb == 0 && (nx != nprune - || (curres = curres == 0 ? mod-1 : curres-1) == 0)) - { - for (j = 0; j < n; ++j) - degx[j] = deg[j]; - if (data[nx].ne != ne || data[nx].dmax != 0) - xbnds(nx,ne,0); - xlbx = data[nx].xlb; - xubx = data[nx].xub; - - data[nx].lo = data[nx].xstart[xlbx]; - data[nx].hi = data[nx].xstart[xubx+1]; - if (xubx >= xlbx && accept1(g,n,0,gx,degx,&rigidx)) - { -#ifdef INSTRUMENT - haschild = TRUE; -#endif - tfextend(gx,nx,degx,ne,rigidx,xlbx,xubx); - } - } - - if (xub > 0) - for (v = 0; v < n; ++v) - { - bitv = xbit[v]; - hv = h[v]; - - for (ixx = nchild; --ixx >= 0;) - if ((hv & xx[ixx]) == 0) - { - x = xx[ixx] | bitv; - xc = XPOPCOUNT(x); - if (xc < xub) xx[nchild++] = x; - - if (xc >= xlb && (rigid || xorb[xinv[x]] == xinv[x]) - // CHANGE added parens around last && to eliminate compile warning - && (xc > dmax || (xc == dmax && (x & d) == 0))) - { - if (nx == nprune) - { - if (curres == 0) curres = mod; - if (--curres != 0) continue; - } - for (j = 0; j < n; ++j) - degx[j] = deg[j]; - if (data[nx].ne != ne+xc || data[nx].dmax != xc) - xbnds(nx,ne+xc,xc); - xlbx = data[nx].xlb; - xubx = data[nx].xub; - - data[nx].lo = data[nx].xstart[xlbx]; - data[nx].hi = data[nx].xstart[xubx+1]; - - if (xubx >= xlbx && accept1(g,n,x,gx,degx,&rigidx)) - { -#ifdef INSTRUMENT - haschild = TRUE; -#endif - tfextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx); - } - } - } - } - } -#ifdef INSTRUMENT - if (haschild) ++fertilenodes[n]; -#endif -} - -/**************************************************************************/ - -static void -bipextend(g,n,deg,ne,rigid,xlb,xub) /* extend from n to n+1 */ -graph *g; /* version for bipartite graphs */ -int n,*deg,ne,xlb,xub; -boolean rigid; -{ - register int x,d,bitv,hv; - int *xinv,*xorb,xc; - int nx,i,j,dmax,xlbx,xubx; - graph gx[MAXN]; - int h[MAXN],*xx,v,ixx,nchild; - int degx[MAXN]; - boolean rigidx; - -#ifdef INSTRUMENT - boolean haschild; - - haschild = FALSE; - ++nodes[n]; - if (rigid) ++rigidnodes[n]; -#endif - - nx = n + 1; - dmax = deg[n-1]; - d = xbit[n-1]; - for (i = 0; i < n-1; ++i) - if (deg[i] == dmax) d |= xbit[i]; - - if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb; - if (xlb > xub) return; - - xinv = data[n].xinv; - xorb = data[n].xorb; - xx = data[n].xx; - - makebgraph(g,h,n); - xx[0] = 0; - nchild = 1; - - if (nx == maxn) - { - if (xlb == 0 && !connec && (nx != nprune - || (curres = curres == 0 ? mod-1 : curres-1) == 0) - && accept2(g,n,0,gx,deg,FALSE)) - { - ++count[ne]; -#ifdef INSTRUMENT - haschild = TRUE; -#endif - (*outproc)(outfile,canonise ? gcan : gx,nx); - } - - if (xub > 0) - for (v = 0; v < n; ++v) - { - bitv = xbit[v]; - hv = h[v]; - - for (ixx = nchild; --ixx >= 0;) - if ((hv & xx[ixx]) == 0) - { - x = xx[ixx] | bitv; - xc = XPOPCOUNT(x); - if (xc < xub) xx[nchild++] = x; - - if (xc >= xlb && (rigid || xorb[xinv[x]] == xinv[x]) - // CHANGE added parens around last && to eliminate compile warning - && (xc > dmax || (xc == dmax && (x & d) == 0))) - { - if ((nx != nprune - || (curres = curres == 0 ? mod-1 : curres-1) == 0) - && accept2(g,n,x,gx,deg, - // CHANGE added parens around last && to eliminate compile warning - xc > dmax+1 || (xc == dmax+1 && (x & d) == 0)) - && (!connec || isconnected(gx,nx))) - { - ++count[ne+xc]; -#ifdef INSTRUMENT - haschild = TRUE; -#endif - (*outproc)(outfile,canonise ? gcan : gx,nx); - } - } - } - } - } - else - { - if (xlb == 0 && (nx != nprune - || (curres = curres == 0 ? mod-1 : curres-1) == 0)) - { - for (j = 0; j < n; ++j) - degx[j] = deg[j]; - if (data[nx].ne != ne || data[nx].dmax != 0) - xbnds(nx,ne,0); - xlbx = data[nx].xlb; - xubx = data[nx].xub; - - data[nx].lo = data[nx].xstart[xlbx]; - data[nx].hi = data[nx].xstart[xubx+1]; - if (xubx >= xlbx && accept1(g,n,0,gx,degx,&rigidx)) - { -#ifdef INSTRUMENT - haschild = TRUE; -#endif - bipextend(gx,nx,degx,ne,rigidx,xlbx,xubx); - } - } - - if (xub > 0) - for (v = 0; v < n; ++v) - { - bitv = xbit[v]; - hv = h[v]; - - for (ixx = nchild; --ixx >= 0;) - if ((hv & xx[ixx]) == 0) - { - x = xx[ixx] | bitv; - xc = XPOPCOUNT(x); - if (xc < xub) xx[nchild++] = x; - - if (xc >= xlb && (rigid || xorb[xinv[x]] == xinv[x]) - // CHANGE added parens around last && to eliminate compile warning - && (xc > dmax || (xc == dmax && (x & d) == 0))) - { - if (nx == nprune) - { - if (curres == 0) curres = mod; - if (--curres != 0) continue; - } - for (j = 0; j < n; ++j) - degx[j] = deg[j]; - if (data[nx].ne != ne+xc || data[nx].dmax != xc) - xbnds(nx,ne+xc,xc); - xlbx = data[nx].xlb; - xubx = data[nx].xub; - - data[nx].lo = data[nx].xstart[xlbx]; - data[nx].hi = data[nx].xstart[xubx+1]; - - if (xubx >= xlbx && accept1(g,n,x,gx,degx,&rigidx)) - { -#ifdef INSTRUMENT - haschild = TRUE; -#endif - bipextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx); - } - } - } - } - } -#ifdef INSTRUMENT - if (haschild) ++fertilenodes[n]; -#endif -} - -/**************************************************************************/ - -static void -genextend(g,n,deg,ne,rigid,xlb,xub) /* extend from n to n+1 */ -graph *g; /* version for general graphs */ -int n,*deg,ne,xlb,xub; -boolean rigid; -{ - register int x,d; - int *xset,*xcard,*xorb,xc; - int nx,i,j,imin,imax,dmax; - int xlbx,xubx; - graph gx[MAXN]; - int degx[MAXN]; - boolean rigidx; - -#ifdef INSTRUMENT - boolean haschild; - - haschild = FALSE; - ++nodes[n]; - if (rigid) ++rigidnodes[n]; -#endif - - nx = n + 1; - dmax = deg[n-1]; - d = xbit[n-1]; - for (i = 0; i < n-1; ++i) - if (deg[i] == dmax) d |= xbit[i]; - - if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb; - if (xlb > xub) return; - - imin = data[n].xstart[xlb]; - imax = data[n].xstart[xub+1]; - xset = data[n].xset; - xcard = data[n].xcard; - xorb = data[n].xorb; - - if (nx == maxn) - for (i = imin; i < imax; ++i) - { - if (!rigid && xorb[i] != i) continue; - x = xset[i]; - xc = xcard[i]; - if (xc == dmax && (x & d) != 0) continue; - - if (nx == nprune) - { - if (curres == 0) curres = mod; - if (--curres != 0) continue; - } - if (accept2(g,n,x,gx,deg, - // CHANGE added parens around last && to eliminate compile warning - xc > dmax+1 || (xc == dmax+1 && (x & d) == 0))) - if (!connec || isconnected(gx,nx)) - { - ++count[ne+xc]; -#ifdef INSTRUMENT - haschild = TRUE; -#endif - (*outproc)(outfile,canonise ? gcan : gx,nx); - } - } - else - for (i = imin; i < imax; ++i) - { - if (!rigid && xorb[i] != i) continue; - x = xset[i]; - xc = xcard[i]; - if (xc == dmax && (x & d) != 0) continue; - if (nx == nprune) - { - if (curres == 0) curres = mod; - if (--curres != 0) continue; - } - for (j = 0; j < n; ++j) - degx[j] = deg[j]; - if (data[nx].ne != ne+xc || data[nx].dmax != xc) - xbnds(nx,ne+xc,xc); - xlbx = data[nx].xlb; - xubx = data[nx].xub; - if (xlbx > xubx) continue; - - data[nx].lo = data[nx].xstart[xlbx]; - data[nx].hi = data[nx].xstart[xubx+1]; - if (accept1(g,n,x,gx,degx,&rigidx)) - { -#ifdef INSTRUMENT - haschild = TRUE; -#endif - genextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx); - } - } -#ifdef INSTRUMENT - if (haschild) ++fertilenodes[n]; -#endif -} - -/**************************************************************************/ -/**************************************************************************/ - -// CHANGE start -// Added this include file and extern... -#include "outproc.h" -extern int errorFound; -// And changed main to makeg_main, changed prototype declaration, -// and added command char (e.g. p=planarity, d=planar drawing, -// o=outerplanarity, 2=K2,3 search, 3=K3,3 search, ... -int makeg_main(char command, int argc, char *argv[]) -// CHANGE end -{ - char *arg; - long ltemp; - boolean badargs; - int i,argsgot; - graph g[1]; - int deg[1]; - long nout; - double t1,t2; - - badargs = FALSE; - connec = FALSE; - trianglefree = FALSE; - bipartite = FALSE; - verbose = FALSE; - nautyformat = FALSE; - nooutput = FALSE; - canonise = FALSE; - - maxdeg = MAXN; - - argsgot = 0; - for (i = 1; !badargs && i < argc; ++i) - { - arg = argv[i]; - if (arg[0] == '-' && arg[1] != '\0') - { - if (arg[1] == 'c' || arg[1] == 'C') connec = TRUE; - else if (arg[1] == 'd' || arg[1] == 'D') - { - if (sscanf(arg+2,"%d",&maxdeg) != 1) badargs = TRUE; - } - else if (arg[1] == 'n' || arg[1] == 'N') nautyformat = TRUE; - else if (arg[1] == 'u' || arg[1] == 'U') nooutput = TRUE; - else if (arg[1] == 't' || arg[1] == 'T') trianglefree = TRUE; - else if (arg[1] == 'b' || arg[1] == 'B') bipartite = TRUE; - else if (arg[1] == 'v' || arg[1] == 'V') verbose = TRUE; - else if (arg[1] == 'l' || arg[1] == 'L') canonise = TRUE; - else badargs = TRUE; - } - else - { - if (argsgot > 4) - badargs = TRUE; - else - { - if (sscanf(arg,"%ld",<emp) != 1) badargs = TRUE; - else if (argsgot == 0) maxn = ltemp; - else if (argsgot == 1) mine = ltemp; - else if (argsgot == 2) maxe = ltemp; - else if (argsgot == 3) mod = ltemp; - else if (argsgot == 4) res = ltemp; - } - ++argsgot; - } - } - - if (argsgot == 0) - badargs = TRUE; - else if (maxn < 1 || maxn > MAXN) - { - fprintf(stderr,">E makeg: n must be in the range 1..%d\n",MAXN); - badargs = TRUE; - } - - if (argsgot == 1) - { - mine = 0; - maxe = (maxn*maxn - maxn) / 2; - } - else if (argsgot == 2) - maxe = mine; - - if (argsgot <= 3) - { - mod = 1; - res = 0; - } - else if (argsgot == 4 || argsgot > 5) - badargs = TRUE; - - if (maxdeg >= maxn) maxdeg = maxn - 1; - if (maxe > maxn*maxdeg / 2) maxe = maxn*maxdeg / 2; - - if (!badargs && (mine > maxe || maxe < 0 || maxdeg < 0)) - { - fprintf(stderr,">E makeg: impossible mine,maxe,maxdeg values\n"); - badargs = TRUE; - } - - if (!badargs && (res < 0 || res >= mod)) - { - fprintf(stderr,">E makeg: must have 0 <= res < mod\n"); - badargs = TRUE; - } - - if (connec && mine < maxn-1) mine = maxn - 1; - if (bipartite) trianglefree = TRUE; - if (trianglefree && maxe > (maxn/2)*(maxn-maxn/2)) - maxe = (maxn/2)*(maxn-maxn/2); - - if (badargs) - { - fprintf(stderr, -">E Usage: makeg [-c -t -b -n -u -v -l] [-d] n [mine [maxe [mod res]]]\n"); -// CHANGE start - //exit(2); - return 2; -// CHANGE end - } - -// CHANGE start - g_maxn = maxn; - g_mine = mine; - g_maxe = maxe; - g_mod = mod; - g_res = res; - g_command = command; - errorFound = 0; - outproc = outprocTest; -// if (nautyformat) outproc = writenauty; -// else if (nooutput) outproc = nullwrite; -// else outproc = writeny; -// CHANGE end - - for (i = 0; i <= maxe; ++i) - count[i] = 0; - - msgfile = stderr; - outfile = stdout; -// CHANGE start - g_msgfile = msgfile; -// CHANGE end - -// CHANGE start (commented this out) -// fprintf(msgfile,">A n=%d e=%d:%d d=%d class=%d/%d\n", -// maxn,mine,maxe,maxdeg,mod,res); -// CHANGE end - - g[0] = 0; - deg[0] = 0; - - t1 = CPUTIME; - - if (maxn == 1) - { - if (res == 0) - { - ++count[0]; - (*outproc)(outfile,g,1); - } - } - else - { - makeleveldata(); - curres = res; - if (mod <= 1) nprune = 0; - else if (maxn >= 9) nprune = maxn - 2; - else if (maxn >= 7) nprune = maxn - 1; - else nprune = maxn; - - xbnds(1,0,0); - if (bipartite) - bipextend(g,1,deg,0,TRUE,data[1].xlb,data[1].xub); - else if (trianglefree) - tfextend(g,1,deg,0,TRUE,data[1].xlb,data[1].xub); - else - genextend(g,1,deg,0,TRUE,data[1].xlb,data[1].xub); - } - t2 = CPUTIME; - - nout = 0; - for (i = 0; i <= maxe; ++i) - nout += count[i]; - - if (verbose) - for (i = 0; i <= maxe; ++i) - if (count[i] > 0) - fprintf(msgfile, - ">C %7ld graphs with %d edges\n",count[i],i); - -#ifdef INSTRUMENT - fprintf(msgfile,"\n>N node counts\n"); - for (i = 1; i < maxn; ++i) - fprintf(msgfile," level %2d: %7ld (%ld rigid, %ld fertile)\n", - i,nodes[i],rigidnodes[i],fertilenodes[i]); - fprintf(msgfile,">A1 %ld calls to accept1, %ld nauty, %ld succeeded\n", - a1calls,a1nauty,a1succs); - fprintf(msgfile, - ">A2 %ld calls to accept2, %ld nuniq, %ld nauty, %ld succeeded\n", - a2calls,a2uniq,a2nauty,a2succs); - fprintf(msgfile,"\n"); -#endif - -// CHANGE start - Test_PrintStats(outfile); -// CHANGE end - -// CHANGE start (commented this out) -// fprintf(msgfile,">Z %ld graphs generated in %3.2f sec\n",nout,t2-t1); -// CHANGE end - -// CHANGE start - return 0; - //exit(0); -// CHANGE end -} - diff --git a/planarity_src/c/nauty/nautil.c b/planarity_src/c/nauty/nautil.c deleted file mode 100644 index 0299506..0000000 --- a/planarity_src/c/nauty/nautil.c +++ /dev/null @@ -1,1251 +0,0 @@ -/***************************************************************************** -* * -* Auxiliary source file for version 1.9 of nauty. * -* * -* Copyright (1984-1993) Brendan McKay. All rights reserved. * -* Subject to waivers and disclaimers in nauty.h. * -* * -* CHANGE HISTORY * -* 10-Nov-87 : final changes for version 1.2 * -* 5-Dec-87 : renamed to version 1.3 (no changes to this file) * -* 28-Sep-88 : renamed to version 1.4 (no changes to this file) * -* 23-Mar-89 : changes for version 1.5 : * -* - added procedure refine1() * -* - changed type of ptn from int* to nvector* in fmptn() * -* - declared level in breakout() * -* - changed char[] to char* in a few places * -* - minor rearrangement in bestcell() * -* 31-Mar-89 : - added procedure doref() * -* 5-Apr-89 : - changed MAKEEMPTY uses to EMPTYSET * -* 12-Apr-89 : - changed writeperm() and fmperm() to not use MARKing * -* 5-May-89 : - redefined MASH to gain about 8% efficiency * -* 18-Oct-90 : changes for version 1.6 : * -* - improved line breaking in writeperm() * -* 10-Nov-90 : - added dummy routine nautil_null() * -* 27-Aug-92 : changes for version 1.7 : * -* - made linelength <= 0 mean no line breaks * -* 5-Jun-93 : renamed to version 1.7+ (no changes to this file) * -* 18-Aug-93 : renamed to version 1.8 (no changes to this file) * -* 17-Sep-93 : renamed to version 1.9 (no changes to this file) * -* 29-Jun-95 : changes for version 1.10 : * -* - replaced loop in nextelement() to save reference past * -* end of array (thanks to Kevin Maylsiak) * -* * -*****************************************************************************/ - -#define EXTDEFS 1 -#include "nauty.h" - - /* macros for hash-codes: */ -#define MASH(l,i) ((((l) ^ 065435) + (i)) & 077777) - /* : expression whose long value depends only on long l and int/long i. - Anything goes, preferably non-commutative. */ - -#define CLEANUP(l) ((int)((l) % INFINITY)) - /* : expression whose value depends on long l and is less than INFINITY - when converted to int then short. Anything goes. */ - -#if MAXM==1 -#define M 1 -#else -#define M m -#endif - -static set workset[MAXM]; /* used for scratch work */ -static permutation workperm[MAXN]; -static short bucket[MAXN+2]; - -/***************************************************************************** -* * -* nextelement(set1,m,pos) = the position of the first element in set set1 * -* which occupies a position greater than pos. If no such element exists, * -* the value is -1. pos can have any value less than n, including negative * -* values. * -* * -* GLOBALS ACCESSED: none * -* * -*****************************************************************************/ - -int -nextelement(set1,m,pos) -register set *set1; -int m,pos; -{ - register setword setwd; - register int w; - -#if MAXM==1 - if (pos < 0) - setwd = set1[0]; - else - setwd = set1[0] & BITMASK(pos); - if (setwd == 0) - return(-1); - else - return(FIRSTBIT(setwd)); -#else - if (pos < 0) - { - w = 0; - setwd = set1[0]; - } - else - { - w = SETWD(pos); - setwd = set1[w] & BITMASK(SETBT(pos)); - } -/* - do - { - if (setwd != 0) - return(TIMESWORDSIZE(w) + FIRSTBIT(setwd)); - setwd = set1[++w]; - } - while (w < m); - - return(-1); -*/ - for (;;) - { - if (setwd != 0) - return(TIMESWORDSIZE(w) + FIRSTBIT(setwd)); - if (++w == m) return -1; - setwd = set1[w]; - } - -#endif -} - -/***************************************************************************** -* * -* permset(set1,set2,m,perm) defines set2 to be the set * -* {perm[i] | i in set1}. * -* * -* GLOBALS ACCESSED: bit,leftbit * -* * -*****************************************************************************/ - -void -permset(set1,set2,m,perm) -set *set1,*set2; -permutation *perm; -int m; -{ - register setword setw; - register int pos,w,b; - - EMPTYSET(set2,m); - setw = set1[0]; - -#if MAXM==1 - while (setw != 0) - { - b = FIRSTBIT(setw); - ZAPBIT(setw,b); - pos = perm[b]; - ADDELEMENT(set2,pos); - } -#else - w = 0; - do - { - while (setw != 0) - { - b = FIRSTBIT(setw); - ZAPBIT(setw,b); - pos = perm[TIMESWORDSIZE(w) + b]; - ADDELEMENT(set2,pos); - } - setw = set1[++w]; - } - while (w < m); -#endif -} - -/***************************************************************************** -* * -* isautom(g,perm,digraph,m,n) = TRUE iff perm is an automorphism of g * -* (i.e., g^perm = g). Symmetry is assumed unless digraph = TRUE. * -* * -* GLOBALS ACCESSED: bit,nextelement() * -* * -*****************************************************************************/ - -boolean -isautom(g,perm,digraph,m,n) -graph *g; -register permutation *perm; -boolean digraph; -int m,n; -{ - register set *pg; - register int pos; - set *pgp; - int posp,i; - - for (pg = g, i = 0; i < n; pg += M, ++i) - { - pgp = GRAPHROW(g,perm[i],M); - pos = (digraph ? -1 : i); - - while ((pos = nextelement(pg,M,pos)) >= 0) - { - posp = perm[pos]; - if (!ISELEMENT(pgp,posp)) - return(FALSE); - } - } - return(TRUE); -} - -/***************************************************************************** -* * -* putstring(f,s) writes the nul-terminated string s to file f. * -* * -*****************************************************************************/ - -void -putstring(f,s) -FILE *f; -register char *s; -{ - while (*s != '\0') - { - PUTC(*s,f); - ++s; - } -} - -/***************************************************************************** -* * -* itos(i,s) converts the int i to a nul-terminated decimal character * -* string s. The value returned is the number of characters excluding * -* the nul. * -* * -* GLOBALS ACCESSED: NONE * -* * -*****************************************************************************/ - -int -itos(i,s) -register int i; -register char *s; -{ - register int digit,j,k; - register char c; - int ans; - - if (i < 0) - { - k = 0; - i = -i; - j = 1; - s[0] = '-'; - } - else - { - k = -1; - j = 0; - } - - do - { - digit = i % 10; - i = i / 10; - s[++k] = digit + '0'; - } - while (i); - - s[k+1] = '\0'; - ans = k + 1; - - for (;j < k; ++j, --k) - { - c = s[j]; - s[j] = s[k]; - s[k] = c; - } - - return(ans); -} - -/***************************************************************************** -* * -* orbits represents a partition of {0,1,...,n-1}, by orbits[i] = the * -* smallest element in the same cell as i. orbjoin(orbits,autom,n) updates * -* the partition orbits to the join of its current value and the cycle * -* partition of perm. The function value returned is the new number of * -* cells. * -* * -* GLOBALS ACCESSED: NONE * -* * -*****************************************************************************/ - -int -orbjoin(orbits,perm,n) -register nvector *orbits; -permutation *perm; -int n; -{ - register int i,j1,j2; - - for (i = 0; i < n; ++i) - { - j1 = orbits[i]; - while (orbits[j1] != j1) - j1 = orbits[j1]; - j2 = orbits[perm[i]]; - while (orbits[j2] != j2) - j2 = orbits[j2]; - - if (j1 < j2) - orbits[j2] = j1; - else if (j1 > j2) - orbits[j1] = j2; - } - - j1 = 0; - for (i = 0; i < n; ++i) - if ((orbits[i] = orbits[orbits[i]]) == i) - ++j1; - - return(j1); -} - -/***************************************************************************** -* * -* writeperm(f,perm,cartesian,linelength,n) writes the permutation perm to * -* the file f. The cartesian representation (i.e. perm itself) is used if * -* cartesian != FALSE; otherwise the cyclic representation is used. No * -* more than linelength characters (not counting '\n') are written on each * -* line, unless linelength is ridiculously small. linelength<=0 causes no * -* line breaks at all to be made. The global int labelorg is added to each * -* vertex number. * -* * -* GLOBALS ACCESSED: itos(),putstring() * -* * -*****************************************************************************/ - -void -writeperm(f,perm,cartesian,linelength,n) -FILE *f; -permutation *perm; -boolean cartesian; -int linelength,n; -{ - register int i,k,l,curlen,intlen; - char s[30]; - - /* CONDNL(x) writes end-of-line and 3 spaces if x characters - won't fit on the current line. */ -#define CONDNL(x) if (linelength>0 && curlen+(x)>linelength)\ - {putstring(f,"\n ");curlen=3;} - - curlen = 0; - if (cartesian) - { - for (i = 0; i < n; ++i) - { - intlen = itos(perm[i] + labelorg,s); - CONDNL(intlen+1); - PUTC(' ',f); - putstring(f,s); - curlen += intlen + 1; - } - PUTC('\n',f); - } - else - { - for (i = n; --i >= 0;) - workperm[i] = 0; - - for (i = 0; i < n; ++i) - { - if (workperm[i] == 0 && perm[i] != i) - { - l = i; - intlen = itos(l + labelorg,s); - if (curlen > 3) - CONDNL(2*intlen+4); - PUTC('(',f); - do - { - putstring(f,s); - curlen += intlen + 1; - k = l; - l = perm[l]; - workperm[k] = 1; - if (l != i) - { - intlen = itos(l + labelorg,s); - CONDNL(intlen+2); - PUTC(' ',f); - } - } - while (l != i); - PUTC(')',f); - ++curlen; - } - } - - if (curlen == 0) - putstring(f,"(1)\n"); - else - PUTC('\n',f); - } -} - -/***************************************************************************** -* * -* testcanlab(g,canong,lab,samerows,m,n) compares g^lab to canong, * -* using an ordering which is immaterial since it's only used here. The * -* value returned is -1,0,1 if g^lab <,=,> canong. *samerows is set to * -* the number of rows (0..n) of canong which are the same as those of g^lab. * -* * -* GLOBALS ACCESSED: workset,permset(),workperm * -* * -*****************************************************************************/ - -int -testcanlab(g,canong,lab,samerows,m,n) -graph *g,*canong; -nvector *lab; -int m,n,*samerows; -{ - register int i,j; - register set *ph; - - for (i = 0; i < n; ++i) - workperm[lab[i]] = i; - - for (i = 0, ph = canong; i < n; ++i, ph += M) - { - permset(GRAPHROW(g,lab[i],M),workset,M,workperm); - for (j = 0; j < M; ++j) - if (workset[j] < ph[j]) - { - *samerows = i; - return(-1); - } - else if (workset[j] > ph[j]) - { - *samerows = i; - return(1); - } - } - - *samerows = n; - return(0); -} - -/***************************************************************************** -* * -* updatecan(g,canong,lab,samerows,m,n) sets canong = g^lab, assuming * -* the first samerows of canong are ok already. * -* * -* GLOBALS ACCESSED: permset(),workperm * -* * -*****************************************************************************/ - -void -updatecan(g,canong,lab,samerows,m,n) -graph *g,*canong; -permutation *lab; -int m,n,samerows; -{ - register int i; - register set *ph; - - for (i = 0; i < n; ++i) - workperm[lab[i]] = i; - - for (i = samerows, ph = GRAPHROW(canong,samerows,M); - i < n; ++i, ph += M) - permset(GRAPHROW(g,lab[i],M),ph,M,workperm); -} - -/***************************************************************************** -* * -* fmperm(perm,fix,mcr,m,n) uses perm to construct fix and mcr. fix * -* contains those points are fixed by perm, while mcr contains the set of * -* those points which are least in their orbits. * -* * -* GLOBALS ACCESSED: bit * -* * -*****************************************************************************/ - -void -fmperm(perm,fix,mcr,m,n) -register permutation *perm; -set *fix,*mcr; -int m,n; -{ - register int i,k,l; - - EMPTYSET(fix,m); - EMPTYSET(mcr,m); - - for (i = n; --i >= 0;) - workperm[i] = 0; - - for (i = 0; i < n; ++i) - if (perm[i] == i) - { - ADDELEMENT(fix,i); - ADDELEMENT(mcr,i); - } - else if (workperm[i] == 0) - { - l = i; - do - { - k = l; - l = perm[l]; - workperm[k] = 1; - } - while (l != i); - - ADDELEMENT(mcr,i); - } -} - -/***************************************************************************** -* * -* fmptn(lab,ptn,level,fix,mcr,m,n) uses the partition at the specified * -* level in the partition nest (lab,ptn) to make sets fix and mcr. fix * -* represents the points in trivial cells of the partition, while mcr * -* represents those points which are least in their cells. * -* * -* GLOBALS ACCESSED: bit * -* * -*****************************************************************************/ - -void -fmptn(lab,ptn,level,fix,mcr,m,n) -nvector *lab,*ptn; -register int level; -set *fix,*mcr; -int m,n; -{ - register int i,lmin; - - EMPTYSET(fix,m); - EMPTYSET(mcr,m); - - for (i = 0; i < n; ++i) - if (ptn[i] <= level) - { - ADDELEMENT(fix,lab[i]); - ADDELEMENT(mcr,lab[i]); - } - else - { - lmin = lab[i]; - do - if (lab[++i] < lmin) - lmin = lab[i]; - while (ptn[i] > level); - ADDELEMENT(mcr,lmin); - } -} - -/***************************************************************************** -* * -* refine(g,lab,ptn,level,numcells,count,active,code,m,n) performs a * -* refinement operation on the partition at the specified level of the * -* partition nest (lab,ptn). *numcells is assumed to contain the number of * -* cells on input, and is updated. The initial set of active cells (alpha * -* in the paper) is specified in the set active. Precisely, x is in active * -* iff the cell starting at index x in lab is active. * -* The resulting partition is equitable if active is correct (see the paper * -* and the Guide). * -* *code is set to a value which depends on the fine detail of the * -* algorithm, but which is independent of the labelling of the graph. * -* count is used for work space. * -* * -* GLOBALS ACCESSED: workset,bit,nextelement(),bucket,workperm * -* * -*****************************************************************************/ - -UPROC -refine(g,lab,ptn,level,numcells,count,active,code,m,n) -graph *g; -register nvector *lab,*ptn; -permutation *count; -int *numcells,level,m,n,*code; -set *active; -{ - register int i,c1,c2,labc1; - register setword x; - register set *set1,*set2; - int split1,split2,cell1,cell2; - int cnt,bmin,bmax; - long longcode; - set *gptr; - - longcode = *numcells; - split1 = -1; - - while (*numcells < n && ((split1 = nextelement(active,M,split1)) >= 0 - || (split1 = nextelement(active,M,-1)) >= 0)) - { - DELELEMENT(active,split1); - for (split2 = split1; ptn[split2] > level; ++split2) - {} - longcode = MASH(longcode,split1 + split2); - if (split1 == split2) /* trivial splitting cell */ - { - gptr = GRAPHROW(g,lab[split1],M); - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - for (cell2 = cell1; ptn[cell2] > level; ++cell2) - {} - if (cell1 == cell2) - continue; - c1 = cell1; - c2 = cell2; - while (c1 <= c2) - { - labc1 = lab[c1]; - if (ISELEMENT(gptr,labc1)) - ++c1; - else - { - lab[c1] = lab[c2]; - lab[c2] = labc1; - --c2; - } - } - if (c2 >= cell1 && c1 <= cell2) - { - ptn[c2] = level; - longcode = MASH(longcode,c2); - ++*numcells; - ADDELEMENT(active,c1); - } - } - } - - else /* nontrivial splitting cell */ - { - EMPTYSET(workset,m); - for (i = split1; i <= split2; ++i) - ADDELEMENT(workset,lab[i]); - longcode = MASH(longcode,split2 - split1 + 1); - - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - for (cell2 = cell1; ptn[cell2] > level; ++cell2) - {} - if (cell1 == cell2) - continue; - i = cell1; -#if MAXM==1 - if (x = workset[0] & g[lab[i]]) /* not == */ - cnt = POPCOUNT(x); - else - cnt = 0; -#else - set1 = workset; - set2 = GRAPHROW(g,lab[i],m); - cnt = 0; - for (c1 = m; --c1 >= 0;) - // CHANGE added extra parens around assignment to eliminate compile warning - if ((x = (*set1++) & (*set2++))) /* not == */ - cnt += POPCOUNT(x); -#endif - count[i] = bmin = bmax = cnt; - bucket[cnt] = 1; - while (++i <= cell2) - { -#if MAXM==1 - if (x = workset[0] & g[lab[i]]) /* not == */ - cnt = POPCOUNT(x); - else - cnt = 0; -#else - set1 = workset; - set2 = GRAPHROW(g,lab[i],m); - cnt = 0; - for (c1 = m; --c1 >= 0;) - // CHANGE added extra parens around assignment to eliminate compile warning - if ((x = (*set1++) & (*set2++))) /* not == */ - cnt += POPCOUNT(x); -#endif - while (bmin > cnt) - bucket[--bmin] = 0; - while (bmax < cnt) - bucket[++bmax] = 0; - ++bucket[cnt]; - count[i] = cnt; - } - if (bmin == bmax) - { - longcode = MASH(longcode,bmin + cell1); - continue; - } - c1 = cell1; - for (i = bmin; i <= bmax; ++i) - if (bucket[i]) - { - c2 = c1 + bucket[i]; - bucket[i] = c1; - longcode = MASH(longcode,i + c1); - if (c1 != cell1) - { - ADDELEMENT(active,c1); - ++*numcells; - } - if (c2 <= cell2) - ptn[c2-1] = level; - c1 = c2; - } - for (i = cell1; i <= cell2; ++i) - workperm[bucket[count[i]]++] = lab[i]; - for (i = cell1; i <= cell2; ++i) - lab[i] = workperm[i]; - } - } - } - - longcode = MASH(longcode,*numcells); - *code = CLEANUP(longcode); -} - -/***************************************************************************** -* * -* refine1(g,lab,ptn,level,numcells,count,active,code,m,n) is the same as * -* refine(g,lab,ptn,level,numcells,count,active,code,m,n), except that * -* m==1 is assumed for greater efficiency. The results are identical in all * -* respects. See refine (above) for the specs. * -* * -*****************************************************************************/ - -UPROC -refine1(g,lab,ptn,level,numcells,count,active,code,m,n) -graph *g; -register nvector *lab,*ptn; -permutation *count; -int *numcells,level,m,n,*code; -set *active; -{ - register int i,c1,c2,labc1; - register setword x; - int split1,split2,cell1,cell2; - int cnt,bmin,bmax; - long longcode; - set *gptr; - - longcode = *numcells; - split1 = -1; - - while (*numcells < n && ((split1 = nextelement(active,1,split1)) >= 0 - || (split1 = nextelement(active,1,-1)) >= 0)) - { - DELELEM1(active,split1); - for (split2 = split1; ptn[split2] > level; ++split2) - {} - longcode = MASH(longcode,split1 + split2); - if (split1 == split2) /* trivial splitting cell */ - { - gptr = GRAPHROW(g,lab[split1],1); - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - for (cell2 = cell1; ptn[cell2] > level; ++cell2) - {} - if (cell1 == cell2) - continue; - c1 = cell1; - c2 = cell2; - while (c1 <= c2) - { - labc1 = lab[c1]; - if (ISELEM1(gptr,labc1)) - ++c1; - else - { - lab[c1] = lab[c2]; - lab[c2] = labc1; - --c2; - } - } - if (c2 >= cell1 && c1 <= cell2) - { - ptn[c2] = level; - longcode = MASH(longcode,c2); - ++*numcells; - ADDELEM1(active,c1); - } - } - } - - else /* nontrivial splitting cell */ - { - *workset = 0; - for (i = split1; i <= split2; ++i) - ADDELEM1(workset,lab[i]); - longcode = MASH(longcode,split2 - split1 + 1); - - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - for (cell2 = cell1; ptn[cell2] > level; ++cell2) - {} - if (cell1 == cell2) - continue; - i = cell1; - // CHANGE added extra parens around assignment to eliminate compile warning - if ((x = workset[0] & g[lab[i]])) /* not == */ - cnt = POPCOUNT(x); - else - cnt = 0; - count[i] = bmin = bmax = cnt; - bucket[cnt] = 1; - while (++i <= cell2) - { - // CHANGE added extra parens around assignment to eliminate compile warning - if ((x = workset[0] & g[lab[i]])) /* not == */ - cnt = POPCOUNT(x); - else - cnt = 0; - while (bmin > cnt) - bucket[--bmin] = 0; - while (bmax < cnt) - bucket[++bmax] = 0; - ++bucket[cnt]; - count[i] = cnt; - } - if (bmin == bmax) - { - longcode = MASH(longcode,bmin + cell1); - continue; - } - c1 = cell1; - for (i = bmin; i <= bmax; ++i) - if (bucket[i]) - { - c2 = c1 + bucket[i]; - bucket[i] = c1; - longcode = MASH(longcode,i + c1); - if (c1 != cell1) - { - ADDELEM1(active,c1); - ++*numcells; - } - if (c2 <= cell2) - ptn[c2-1] = level; - c1 = c2; - } - for (i = cell1; i <= cell2; ++i) - workperm[bucket[count[i]]++] = lab[i]; - for (i = cell1; i <= cell2; ++i) - lab[i] = workperm[i]; - } - } - } - - longcode = MASH(longcode,*numcells); - *code = CLEANUP(longcode); -} - -/***************************************************************************** -* * -* doref(g,lab,ptn,level,numcells,qinvar,invar,active,code,refproc, * -* invarproc,mininvarlev,maxinvarlev,invararg,digraph,m,n) * -* is used to perform a refinement on the partition at the given level in * -* (lab,ptn). The number of cells is *numcells both for input and output. * -* The input active is the active set for input to the refinement procedure * -* (*refproc)(), which must have the argument list of refine(). * -* active may be arbitrarily changed. invar is used for working storage. * -* First, (*refproc)() is called. Then, if invarproc!=NILFUNCTION and * -* |mininvarlev| <= level <= |maxinvarlev|, the routine (*invarproc)() is * -* used to compute a vertex-invariant which may refine the partition * -* further. If it does, (*refproc)() is called again, using an active set * -* containing all but the first fragment of each old cell. Unless g is a * -* digraph, this guarantees that the final partition is equitable. The * -* arguments invararg and digraph are passed to (*invarproc)() * -* uninterpretted. The output argument code is a composite of the codes * -* from all the calls to (*refproc)(). The output argument qinvar is set * -* to 0 if (*invarproc)() is not applied, 1 if it is applied but fails to * -* refine the partition, and 2 if it succeeds. * -* See the file nautinv.c for a further discussion of vertex-invariants. * -* Note that the dreadnaut I command generates a call to this procedure * -* with level = mininvarlevel = maxinvarlevel = 0. * -* * -*****************************************************************************/ - -void -doref(g,lab,ptn,level,numcells,qinvar,invar,active,code,refproc,invarproc, - mininvarlev,maxinvarlev,invararg,digraph,m,n) -graph *g; -nvector *lab,*ptn; -int level,*numcells,*qinvar,*code,mininvarlev,maxinvarlev,invararg,m,n; -boolean digraph; -permutation *invar; -set *active; -UPROC (*refproc)(),(*invarproc)(); -{ - register int j,h; - register permutation pw; - nvector iw; - int i,cell1,cell2,nc,tvpos,minlev,maxlev; - long longcode; - boolean same; - - if ((tvpos = nextelement(active,M,-1)) < 0) - tvpos = 0; - - (*refproc)(g,lab,ptn,level,numcells,invar,active,code,M,n); - - minlev = (mininvarlev < 0 ? -mininvarlev : mininvarlev); - maxlev = (maxinvarlev < 0 ? -maxinvarlev : maxinvarlev); - if (invarproc != NILFUNCTION && *numcells < n - && level >= minlev && level <= maxlev) - { - (*invarproc)(g,lab,ptn,level,*numcells,tvpos,invar,invararg, - digraph,M,n); - EMPTYSET(active,m); - for (i = n; --i >= 0;) - workperm[i] = invar[lab[i]]; - nc = *numcells; - for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) - { - pw = workperm[cell1]; - same = TRUE; - for (cell2 = cell1; ptn[cell2] > level; ++cell2) - if (workperm[cell2+1] != pw) - same = FALSE; - if (same) - continue; - - j = (cell2 - cell1 + 1) / 3; - h = 1; - do - h = 3 * h + 1; - while (h < j); - - do /* shell sort */ - { - for (i = cell1 + h; i <= cell2; ++i) - { - iw = lab[i]; - pw = workperm[i]; - for (j = i; workperm[j-h] > pw; ) - { - workperm[j] = workperm[j-h]; - lab[j] = lab[j-h]; - if ((j -= h) < cell1 + h) - break; - } - workperm[j] = pw; - lab[j] = iw; - } - h /= 3; - } - while (h > 0); - - for (i = cell1 + 1; i <= cell2; ++i) - if (workperm[i] != workperm[i-1]) - { - ptn[i-1] = level; - ++*numcells; - ADDELEMENT(active,i); - } - } - - if (*numcells > nc) - { - *qinvar = 2; - longcode = *code; - (*refproc)(g,lab,ptn,level,numcells,invar,active,code,M,n); - longcode = MASH(longcode,*code); - *code = CLEANUP(longcode); - } - else - *qinvar = 1; - } - else - *qinvar = 0; -} - -/***************************************************************************** -* * -* cheapautom(ptn,level,digraph,n) returns TRUE if the partition at the * -* specified level in the partition nest (lab,ptn) {lab is not needed here} * -* satisfies a simple sufficient condition for its cells to be the orbits of * -* some subgroup of the automorphism group. Otherwise it returns FALSE. * -* It always returns FALSE if digraph!=FALSE. * -* * -* nauty assumes that this function will always return TRUE for any * -* partition finer than one for which it returns TRUE. * -* * -*****************************************************************************/ - -boolean -cheapautom(ptn,level,digraph,n) -register nvector *ptn; -register int level; -boolean digraph; -int n; -{ - register int i,k,nnt; - - if (digraph) - return(FALSE); - - k = n; - nnt = 0; - for (i = 0; i < n; ++i) - { - --k; - if (ptn[i] > level) - { - ++nnt; - while (ptn[++i] > level) - {} - } - } - - return(k <= nnt + 1 || k <= 4); -} - -/***************************************************************************** -* * -* targetcell(g,lab,ptn,level,numcells,tcell,tcellsize,&cellpos,tc_level, * -* hint,m,n) * -* examines the partition at the specified level in the partition nest * -* (lab,ptn) and finds a non-trival cell (if none, the first cell). * -* If hint >= 0 and there is a non-trivial cell starting at position hint * -* in lab, that cell is chosen. * -* Else, If level <= tc_level, bestcell is called to choose a cell. * -* Else, the first non-trivial cell is chosen. * -* When a cell is chosen, tcell is set to its contents, *tcellsize to its * -* size, and cellpos to its starting position in lab. * -* * -* GLOBALS ACCESSED: bit,bestcell() * -* * -*****************************************************************************/ - -UPROC -targetcell(g,lab,ptn,level,numcells,tcell,tcellsize,cellpos,tc_level,hint,m,n) -graph *g; -register nvector *lab,*ptn; -set *tcell; -register int level; -int numcells,*tcellsize,*cellpos,tc_level,hint,m,n; -{ - register int i,j,k; - - if (hint >= 0 && ptn[hint] > level && - (hint == 0 || ptn[hint-1] <= level)) - i = hint; - else if (level <= tc_level) - i = bestcell(g,lab,ptn,level,tc_level,m,n); - else - for (i = 0; i < n && ptn[i] <= level; ++i) - {} - if (i == n) - i = j = 0; - else - for (j = i + 1; ptn[j] > level; ++j) - {} - - *tcellsize = j - i + 1; - - EMPTYSET(tcell,m); - for (k = i; k <= j; ++k) - ADDELEMENT(tcell,lab[k]); - - *cellpos = i; -} - -/***************************************************************************** -* * -* bestcell(g,lab,ptn,level,tc_level,m,n) returns the index in lab of the * -* start of the "best non-singleton cell" for fixing. If there is no * -* non-singleton cell it returns n. * -* This implementation finds the first cell which is non-trivially joined * -* to the greatest number of other cells. * -* * -* GLOBALS ACCESSED: bit,workperm,workset,bucket * -* * -*****************************************************************************/ - -int -bestcell(g,lab,ptn,level,tc_level,m,n) -graph *g; -nvector *lab,*ptn; -int level,tc_level,m,n; -{ - register int i; - set *gp; - register setword setword1,setword2; - int v1,v2,nnt; - - /* find non-singleton cells: put starts in workperm[0..nnt-1] */ - - i = nnt = 0; - - while (i < n) - { - if (ptn[i] > level) - { - workperm[nnt++] = i; - while (ptn[i] > level) - ++i; - } - ++i; - } - - if (nnt == 0) - return(n); - - /* set bucket[i] to # non-trivial neighbours of n.s. cell i */ - - for (i = nnt; --i >= 0;) - bucket[i] = 0; - - for (v2 = 1; v2 < nnt; ++v2) - { - EMPTYSET(workset,m); - i = workperm[v2] - 1; - do - { - ++i; - ADDELEMENT(workset,lab[i]); - } - while (ptn[i] > level); - for (v1 = 0; v1 < v2; ++v1) - { - gp = GRAPHROW(g,lab[workperm[v1]],m); -#if MAXM==1 - setword1 = *workset & *gp; - setword2 = *workset & ~*gp; -#else - setword1 = setword2 = 0; - for (i = m; --i >= 0;) - { - setword1 |= workset[i] & gp[i]; - setword2 |= workset[i] & ~gp[i]; - } -#endif - if (setword1 != 0 && setword2 != 0) - { - ++bucket[v1]; - ++bucket[v2]; - } - } - } - - /* find first greatest bucket value */ - - v1 = 0; - v2 = bucket[0]; - for (i = 1; i < nnt; ++i) - if (bucket[i] > v2) - { - v1 = i; - v2 = bucket[i]; - } - - return((int)workperm[v1]); -} - -/***************************************************************************** -* * -* shortprune(set1,set2,m) ANDs the contents of set set2 into set set1. * -* * -* GLOBALS ACCESSED: NONE * -* * -*****************************************************************************/ - -void -shortprune(set1,set2,m) -register set *set1,*set2; -register int m; -{ - register int i; - - for (i = 0; i < M; ++i) - INTERSECT(set1[i],set2[i]); -} - -/***************************************************************************** -* * -* breakout(lab,ptn,level,tc,tv,active,m) operates on the partition at * -* the specified level in the partition nest (lab,ptn). It finds the * -* element tv, which is in the cell C starting at index tc in lab (it had * -* better be) and splits C in the two cells {tv} and C\{tv}, in that order. * -* It also sets the set active to contain just the element tc. * -* * -* GLOBALS ACCESSED: bit * -* * -*****************************************************************************/ - -void -breakout(lab,ptn,level,tc,tv,active,m) -nvector *lab,*ptn; -set *active; -int level,tc,tv,m; -{ - register int i,prev,next; - - EMPTYSET(active,m); - ADDELEMENT(active,tc); - - i = tc; - prev = tv; - - do - { - next = lab[i]; - lab[i++] = prev; - prev = next; - } - while (prev != tv); - - ptn[tc] = level; -} - -/***************************************************************************** -* * -* longprune(tcell,fix,bottom,top,m) removes zero or elements of the set * -* tcell. It is assumed that addresses bottom through top-1 contain * -* contiguous pairs of sets (f1,m1),(f2,m2), ... . tcell is intersected * -* with each mi such that fi is a subset of fix. * -* * -* GLOBALS ACCESSED: NONE * -* * -*****************************************************************************/ - -void -longprune(tcell,fix,bottom,top,m) -register set *tcell,*fix,*bottom; -set *top; -int m; -{ - register int i; - - while (bottom < top) - { - for (i = 0; i < M; ++i) - if (NOTSUBSET(fix[i],bottom[i])) - break; - bottom += M; - - if (i == M) - for (i = 0; i < M; ++i) - INTERSECT(tcell[i],bottom[i]); - bottom += M; - } -} - -/***************************************************************************** -* * -* nautil_null() does nothing. See dreadnaut.c for its purpose. * -* * -*****************************************************************************/ - -void -nautil_null() -{ -} diff --git a/planarity_src/c/nauty/naututil.h b/planarity_src/c/nauty/naututil.h deleted file mode 100644 index 9cf046b..0000000 --- a/planarity_src/c/nauty/naututil.h +++ /dev/null @@ -1,406 +0,0 @@ -/***************************************************************************** -* * -* This is the header file for versions 1.9+ of naututil.c and dreadnaut.c. * -* * -* Copyright (1984-1993) Brendan McKay. All rights reserved. * -* Subject to the waivers and disclaimers in nauty.h. * -* * -* CHANGE HISTORY * -* 10-Nov-87 : final changes for version 1.2 * -* 5-Dec-87 : changes for version 1.3 : * -* - added declarations of readinteger() and readstring() * -* - added definition of DEFEXT : default file-name * -* extension for dreadnaut input files * -* 28-Sep-88 : changes for version 1.4 : * -* - added support for PC Turbo C * -* 29-Nov-88 : - added getc macro for AZTEC C on MAC * -* 23-Mar-89 : changes for version 1.5 : * -* - added DREADVERSION macro * -* - added optional ANSI function prototypes * -* - changed file name to naututil.h * -* - moved ALLOCS to nauty.h and defined DYNALLOC * -* 25-Mar-89 : - added declaration of twopaths() * -* 29-Mar-89 : - added declaration of putmapping() * -* 4-Apr-89 : - added declarations of triples, quadruples, adjtriang * -* - only define ERRFILE if not in nauty.h * -* 25-Apr-89 : - added declarations of cellquads,distances,getbigcells * -* 26-Apr-89 : - added declarations of indsets,cliques,cellquins * -* - removed declarations of ptncode and equitable * -* 27-Apr-89 : - added declaration of putquotient * -* 18-Aug-89 : - added new arg to putset, and changed mathon * -* 2-Mar-90 : - added declarations of celltrips, cellcliq, cellind * -* - changed declarations to use EXTPROC * -* 12-Mar-90 : - added changes for Cray version * -* 20-Mar-90 : - added changes for THINK version * -* 27-Mar-90 : - split SYS_MSDOS into SYS_PCMS4 and SYS_PCMS5 * -* 13-Oct-90 : changes for version 1.6 : * -* - changed CPUTIME to use HZ on Unix for times() * -* 14-Oct-90 : - added SYS_APOLLO variant * -* 19-Oct-90 : - changed CPUTIME defs for BSDUNIX to avoid conficting * -* declarations of size_t and ptrdiff_t in gcc * -* 27-Aug-92 : changes for version 1.7 : * -* - added SYS_IBMC variant * -* - removed workaround for bad gcc installation * -* 5-Jun-93 : changes for version 1.8 : * -* - changed CRAY version of CPUTIME to use CLK_TCK * -* if HZ could not be found (making 1.7+) * -* 30-Jul-93 : - added SYS_ALPHA variant * -* 17-Sep-93 : changes for version 1.9 : * -* - declared adjacencies() * -* 24-Feb-94 : changes for version 1.10 : * -* - added version SYS_AMIGAAZT (making 1.9+) * -* 19-Apr-95 : - added C++ prototype wrapper * -* * -*****************************************************************************/ - -#include "nauty.h" /* which includes stdio.h */ - -extern long seed; /* declared in naututil.c */ - -#ifdef __cplusplus -extern "C" { -#endif - -EXTPROC(UPROC adjacencies,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC adjtriang,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC cellcliq,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC cellind,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC cellquads,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC cellquins,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC celltrips,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(void cellstarts,(nvector*,int,set*,int,int)) -EXTPROC(UPROC cliques,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(void complement,(graph*,int,int)) -EXTPROC(void copycomment,(FILE*,FILE*,int)) -EXTPROC(UPROC distances,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(void flushline,(FILE*)) -EXTPROC(void fixit,(nvector*,nvector*,int*,int,int)) -EXTPROC(void getbigcells,(nvector*,int,int,int*,short*,short*,int)) -EXTPROC(int getint,(FILE*)) -EXTPROC(long hash,(set*,long,int)) -EXTPROC(UPROC indsets,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(void mathon,(graph*,int,int,graph*,int,int)) -EXTPROC(void putcanon,(FILE*,nvector*,graph*,int,int,int)) -EXTPROC(void putdegs,(FILE*,graph*,int,int,int)) -EXTPROC(void putgraph,(FILE*,graph*,int,int,int)) -EXTPROC(void putmapping,(FILE*,nvector*,int,nvector*,int,int,int)) -EXTPROC(void putorbits,(FILE*,nvector*,int,int)) -EXTPROC(void putptn,(FILE*,nvector*,nvector*,int,int,int)) -EXTPROC(void putquotient,(FILE*,graph*,nvector*,nvector*,int,int,int,int)) -EXTPROC(void putset,(FILE*,set*,int*,int,int,boolean)) -EXTPROC(UPROC quadruples,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(void rangraph,(graph*,boolean,int,int,int)) -EXTPROC(void ranperm,(permutation*,int)) -EXTPROC(void readgraph,(FILE*,graph*,boolean,boolean,boolean,int,int,int)) -EXTPROC(boolean readinteger,(FILE*,int*)) -EXTPROC(void readperm,(FILE*,permutation*,boolean,int)) -EXTPROC(void readptn,(FILE*,nvector*,nvector*,int*,boolean,int)) -EXTPROC(boolean readstring,(FILE*,char*)) -EXTPROC(void relabel,(graph*,nvector*,permutation*,graph*,int,int)) -EXTPROC(int setinter,(set*,set*,int)) -EXTPROC(int setsize,(set*,int)) -EXTPROC(UPROC triples,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(UPROC twopaths,(graph*,nvector*,nvector*,int,int,int,permutation*, - int,boolean,int,int)) -EXTPROC(void unitptn,(nvector*,nvector*,int*,int)) - -#ifdef __cplusplus -} -#endif - -#define DREADVERSION NAUTYVERSION - -/*-------------------------------------------------------------------------*/ - -/* This is the place to work around the system dependencies that dreadnaut ** -** may run into. Most of these involve system calls that not all C ** -** implementations provide. ** -** Changes for nauty.c or nautil.c (hopefully few) should go in nauty.h. ** -** It is assumed that the SYS_ symbols in nauty.h are defined correctly. ** -** To add a new machine, create a new SYS_ symbol and edit both .h files. */ - -/************************************************************************** -(1) dreadnaut prompts to file PROMPTFILE if DOPROMPT(fp) is TRUE, where fp -is the file pointer for the current input file -If you need any extra declarations for this, put then in PROMPTDECLS */ - -#define PROMPTFILE stdout - -#if (IS_UNIX | IS_PC | SYS_VAXVMS | SYS_MACAZT) -EXTPROC(int isatty,(int)) -#define DOPROMPT(fp) (isatty(fileno(fp)) && isatty(fileno(PROMPTFILE))) -#endif - -#if SYS_MACMPW -#include -#define DOPROMPT(fp) (ioctl(fileno(fp),FIOINTERACTIVE,NULL) == 0 && \ - ioctl(fileno(PROMPTFILE),FIOINTERACTIVE,NULL) == 0) -#endif - -#if IS_AMIGA -EXTPROC(int IsInteractive,(int)) -#define DOPROMPT(fp) (IsInteractive(fileno(fp)) && \ - IsInteractive(fileno(PROMPTFILE))) -#endif - -#if (SYS_MACLSC | SYS_MACTHINK | SYS_IBMC | SYS_MISC) -#define DOPROMPT(fp) (curfile==0) /* stdin yes, others no */ -#endif - -/************************************************************************** -(2) dreadnaut writes error messages to ERRFILE. This is allowed to be the -same as PROMPTFILE. There might be a prior definition in nauty.h. */ - -#ifndef ERRFILE -#define ERRFILE stderr -#endif - -/************************************************************************** -(3) Dreadnaut allows up to MAXIFILES input files concurrently open, -including stdin. Allow for up to 4 output files. */ - -#define MAXIFILES 6 - -/************************************************************************** -(4) Dreadnaut uses OPENOUT(fp,name,append) to open output files other than -stdout. fp is the file pointer variable, name is a string containing the -name, and append is a boolean specifying whether an existing file is to be -openned for appending to, or a new file created. The code should leave -fp==NULL if the openning operation failed. */ - -#if SYS_MACMPW -#include -#include -extern OSErr GetFInfo(),SetFInfo(); -#define OPENOUT(fp,name,append) { FInfo fileinfo;\ - if((fp = fopen(name,(append)?"a":"w")) != NULL &&\ - !(append) && GetFInfo(name,0,&fileinfo) == 0)\ - {fileinfo.fdType = 'TEXT'; SetFInfo(name,0,&fileinfo);}} -#else -#define OPENOUT(fp,name,append) fp = fopen(name,(append)?"a":"w") -#endif - -/************************************************************************** -(5) Dreadnaut supposes that CPUTIME is an expression with a double value -equal to the current CPU usage in seconds. If no fine resolution cpu timer -is available, use a real time function. If nothing sensible is possible, -leave CPUTIME undefined. The comma operator is useful here. -CPUDEFS should contain the necessary data declarations, if any. */ - -#if (SYS_VAXVMS | SYS_UNIX | SYS_CRAY) -#define CPUDEFS static struct \ - {int u_time,s_time,cu_time,cs_time;} timebuffer; -#endif - -#if SYS_VAXVMS -#define CPUTIME (times(&timebuffer),\ - (timebuffer.u_time + timebuffer.s_time) * 0.01) -#endif - -#if SYS_UNIX -#ifndef HZ -#include -#ifndef HZ -#define HZ 60 -#endif -#endif -#define CPUTIME (times(&timebuffer),\ - (double)(timebuffer.u_time + timebuffer.s_time) / HZ) -#endif - -#if SYS_CRAY -#ifndef HZ -#include -#ifndef HZ -/* #include */ -#include -#ifndef HZ -#ifdef CLK_TCK -#define HZ CLK_TCK -#endif -#ifndef HZ -#define HZ 1000000 -#endif -#endif -#endif -#endif -#define CPUTIME (times(&timebuffer),\ - (double)(timebuffer.u_time + timebuffer.s_time) / HZ) -#endif - -#if IS_MAC -#define CPUTIME ((double)*(long*)0x16A * 0.0166666667) -#endif - -#if SYS_AMIGALC -#include -#define CPUDEFS static char clk[8]; -#define CPUTIME (getclk(clk),\ - 86400.0*clk[3]+3600.0*clk[4]+60.0*clk[5]+1.0*clk[6]+0.01*clk[7]) -#endif - -#if (SYS_BSDUNIX | SYS_VAXBSD | SYS_APOLLO | SYS_ALPHA) -/* The following definitions were once needed to avoid conficting -declarations in gcc. It seems it was due to bad installation. */ -/* #define size_t SIZ_T_TMP */ -/* #define ptrdiff_t PTRDF_T_TMP */ -#include -#include -extern int getrusage(); -#define CPUDEFS struct rusage ruse; -#define CPUTIME (getrusage(RUSAGE_SELF,&ruse),\ - ruse.ru_utime.tv_sec + ruse.ru_stime.tv_sec + \ - 1e-6 * (ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec)) -/* #undef size_t -- see above */ -/* #undef ptrdiff_t */ -#endif - -#if (SYS_PCMS4 | SYS_PCMS5) -#define CPUDEFS struct timeb {\ - long time;\ - unsigned short millitm;\ - short timezone;\ - short dstflag;};\ - extern int ftime();\ - static struct timeb eusage; -#define CPUTIME (ftime(&eusage),\ - (double)eusage.time+(double)eusage.millitm*0.001) -#endif - -#if SYS_PCTURBO -#include -#define CPUDEFS static struct time tbuf; -#define CPUTIME (gettime(&tbuf), (double)tbuf.ti_hour*3600.0 + \ - (double)tbuf.ti_min*60.0 + (double)tbuf.ti_sec + (double)tbuf.ti_hund*0.01) -#endif - -#if SYS_IBMC -#include -#define CPUTIME ((double) clock() / CLOCKS_PER_SEC) -#endif - -/************************************************************************** -(6) Some implementations don't allow declarations of very large structures -other than dynamically. The problem cases are the graphs g, canong, savedg -and to a lesser extent the work area workspace. -Alternatively, there may be a limit on the total size of statically -allocated data which prevents MAXN from being very large. -dreadnaut uses ALLOCS and FREES (defined in nauty.h) for g, canong and -savedg always. To get it to use ALLOCS for workspace and a bunch of smaller -things as well, define DYNALLOC. */ - -#if (IS_MAC | IS_PC | IS_AMIGA) -#define DYNALLOC 1 -#endif - -/************************************************************************** -(7) dreadnaut uses random numbers for the 'j' and 's' commands. The long -int variable "seed" can be used for this. It is initialised to 1. If -a more random initialisation is available, define INITSEED to do it. -The expression RAN(k) should be a random integer in the range [0..k-1]. -The statistical quality is unimporatant. */ - -#if SYS_AMIGALC -/* #include -- included above in Section 6. */ -#define INITSEED {char clk[8];\ - getclk(clk);\ - seed = 12000*clk[5] + 200*clk[6] + 2*clk[7] + 1;} -#endif - -#if SYS_AMIGAAZT -#include -EXTPROC(unsigned long curticks,(void)) -#define INITSEED seed = curticks() -#endif - -#if IS_MAC -#define INITSEED seed = 2 * *(long*)0x16A + 1 -#endif - -#if (SYS_VAXVMS | IS_UNIX | SYS_PCTURBO) -#if !SYS_ALPHA -EXTPROC(long time,(long*)) -#endif -#define INITSEED seed = ((time((long*)NULL)<<1) | 1) & 017777777777L -#endif - -#define RAN(k) (((seed=(seed*65539L)&017777777777L)/7)%(k)) - -/************************************************************************** -(8) dreadnaut provides nauty with enough space to keep data on at least -WORKSIZE automorphisms. To ensure this, it provides 2 * WORKSIZE * MAXM -setwords. This number must be at most the size of the largest int. -Allow for command-line definition. */ - -#ifndef WORKSIZE -#define WORKSIZE 60 -#endif - -/************************************************************************** -(9) dreadnaut stops execution with EXIT. */ - -#if SYS_VAXVMS -#define EXIT exit(1) -#else -#define EXIT exit(0) -#endif - -/************************************************************************** -(10) dreadnaut allows for extra initialization code to be executed when it -starts up. If anything is needed, put the necesssary decarations in -EXTRADECLS and the initialization code in INITIALIZE. */ - -#if SYS_MACMPW -#include -#define INITIALIZE {if (ioctl(fileno(stdout),FIOINTERACTIVE,0)==0)\ - setvbuf(stdout,NULL,_IOLBF,BUFSIZ);\ - if (prompt) putc('\f',PROMPTFILE);} -#endif - -#if IS_AMIGA -#define INITIALIZE {if (IsInteractive(fileno(stdout))) \ - setvbuf(stdout,NULL,_IOLBF,BUFSIZ);} -#endif - -#if SYS_IBMC -#define INITIALIZE {setbuf(PROMPTFILE,NULL); setbuf(PROMPTFILE,NULL);} -#endif - -/************************************************************************** -(11) Define the symbol NLMAP if '\n' and '\r' are equal. -Leave it undefined otherwise. */ - -#if SYS_MACMPW -#define NLMAP -#endif - -/************************************************************************** -(12) Define DEFEXT to be a sensible file-name extension for dreadnaut -input files. This is used only by the dreadnaut '<' command. -If possible, use either ".dre" or ".DRE". */ - -#define DEFEXT ".dre" - -/************************************************************************** -(13) dreadnaut assumes that '\n' is returned by getc at end of line. It -doesn't matter if '\r' is returned as well, but '\r' by itself won't do. */ - -#if SYS_MACAZT -#define getc agetc -#endif - -/************************************************************************** -(etc) Other things that dreadnaut uses: fprintf, fopen, fclose, ungetc, -putc. */ diff --git a/planarity_src/c/nauty/nauty.c b/planarity_src/c/nauty/nauty.c deleted file mode 100644 index 020b69b..0000000 --- a/planarity_src/c/nauty/nauty.c +++ /dev/null @@ -1,889 +0,0 @@ -/***************************************************************************** -* * -* Main source file for version 1.9 of nauty. * -* * -* Copyright (1984-1993) Brendan McKay. All rights reserved. Permission * -* Subject to the waivers and disclaimers in nauty.h. * -* * -* CHANGE HISTORY * -* 10-Nov-87 : final changes for version 1.2 * -* 5-Dec-87 : renamed to version 1.3 (no changes to this file) * -* 28-Sep-88 : renamed to version 1.4 (no changes to this file) * -* 23-Mar-89 : changes for version 1.5 : * -* - add use of refine1 instead of refine for m==1 * -* - changes for new optionblk syntax * -* - disable tc_level use for digraphs * -* - interposed doref() interface to refine() so that * -* options.invarproc can be supported * -* - declared local routines static * -* 28-Mar-89 : - implemented mininvarlevel/maxinvarlevel < 0 options * -* 2-Apr-89 : - added invarproc fields in stats * -* 5-Apr-89 : - modified error returns from nauty() * -* - added error message to ERRFILE * -* - changed MAKEEMPTY uses to EMPTYSET * -* 18-Apr-89 : - added MTOOBIG and CANONGNIL * -* 8-May-89 : - changed firstcode[] and canoncode[] to short * -* 10-Nov-90 : changes for version 1.6 : * -* - added dummy routine nauty_null (see dreadnaut.c) * -* 2-Sep-91 : changes for version 1.7 : * -* - moved MULTIPLY into nauty.h * -* 27-Mar-92 : - changed 'n' into 'm' in error message in nauty() * -* 5-Jun-93 : renamed to version 1.7+ (no changes to this file) * -* 18-Aug-93 : renamed to version 1.8 (no changes to this file) * -* 17-Sep-93 : renamed to version 1.9 (no changes to this file) * -* * -*****************************************************************************/ - -#define EXTDEFS 1 -#include "nauty.h" - -static int firstpathnode(); -static void firstterminal(); -static int othernode(); -static int processnode(); -static void recover(); -static void writemarker(); - -#if MAXM==1 -#define M 1 -#else -#define M m -#endif - -#define OPTCALL(proc) if (proc != NILFUNCTION) (*proc) - - /* copies of some of the options: */ -static boolean getcanon,digraph,writeautoms,domarkers,cartesian; -static int linelength,tc_level,mininvarlevel,maxinvarlevel,invararg; -static UPROC (*usernodeproc)(),(*userautomproc)(),(*userlevelproc)(), - (*refproc)(),(*tcellproc)(),(*invarproc)(); -static FILE *outfile; - - /* local versions of some of the arguments: */ -static int m,n; -static graph *g,*canong; -static nvector *orbits; -static statsblk *stats; - /* temporary versions of some stats: */ -static long invapplics,invsuccesses; -static int invarsuclevel; - - /* working variables: */ -static int gca_first, /* level of greatest common ancestor of current - node and first leaf */ - gca_canon, /* ditto for current node and bsf leaf */ - noncheaplevel, /* level of greatest ancestor for which cheapautom - == FALSE */ - allsamelevel, /* level of least ancestor of first leaf for - which all descendant leaves are known to be - equivalent */ - eqlev_first, /* level to which codes for this node match those - for first leaf */ - eqlev_canon, /* level to which codes for this node match those - for the bsf leaf. */ - comp_canon, /* -1,0,1 according as code at eqlev_canon+1 is - <,==,> that for bsf leaf. Also used for - similar purpose during leaf processing */ - samerows, /* number of rows of canong which are correct for - the bsf leaf BDM:correct? */ - canonlevel, /* level of bsf leaf */ - stabvertex, /* point fixed in ancestor of first leaf at level - gca_canon */ - cosetindex; /* the point being fixed at level gca_first */ - -static boolean needshortprune; /* used to flag calls to shortprune */ - -static set defltwork[2*MAXM]; /* workspace in case none provided */ -static permutation workperm[MAXN]; /* various scratch uses */ -static set fixedpts[MAXM]; /* points which were explicitly - fixed to get current node */ -static permutation firstlab[MAXN], /* label from first leaf */ - canonlab[MAXN]; /* label from bsf leaf */ -static short firstcode[MAXN+2], /* codes for first leaf */ - canoncode[MAXN+2]; /* codes for bsf leaf */ -static short firsttc[MAXN+2]; /* index of target cell for left path */ -static set active[MAXM]; /* used to contain index to cells now - active for refinement purposes */ -static set *workspace,*worktop; /* first and just-after-last addresses of - work area to hold automorphism data */ -static set *fmptr; /* pointer into workspace */ - -/***************************************************************************** -* * -* This procedure finds generators for the automorphism group of a * -* vertex-coloured graph and optionally finds a canonically labelled * -* isomorph. A description of the data structures can be found in * -* nauty.h and in the "nauty User's Guide". The Guide also gives * -* many more details about its use, and implementation notes. * -* * -* Parameters - means read-only, means write-only, means both: * -* g - the graph * -* lab,ptn - used for the partition nest which defines the colouring * -* of g. The initial colouring will be set by the program, * -* using the same colour for every vertex, if * -* options->defaultptn!=FALSE. Otherwise, you must set it * -* yourself (see the Guide). If options->getcanon!=FALSE, * -* the contents of lab on return give the labelling of g * -* corresponding to canong. This does not change the * -* initial colouring of g as defined by (lab,ptn), since * -* the labelling is consistent with the colouring. * -* active - If this is not NILSET and options->defaultptn==FALSE, * -* it is a set indicating the initial set of active colours. * -* See the Guide for details. * -* orbits - On return, orbits[i] contains the number of the * -* least-numbered vertex in the same orbit as i, for * -* i=0,1,...,n-1. * -* options - A list of options. See nauty.h and/or the Guide * -* for details. * -* stats - A list of statistics produced by the procedure. See * -* nauty.h and/or the Guide for details. * -* workspace - A chunk of memory for working storage. * -* worksize - The number of setwords in workspace. See the Guide * -* for guidance. * -* m - The number of setwords in sets. This must be at * -* least ceil(n / WORDSIZE) and at most MAXM. * -* n - The number of vertices. This must be at least 1 and * -* at most MAXN. * -* canong - The canononically labelled isomorph of g. This is * -* only produced if options->getcanon!=FALSE, and can be * -* given as NILGRAPH otherwise. * -* * -* FUNCTIONS CALLED: firstpathnode(),updatecan() * -* * -*****************************************************************************/ - -void -nauty(g_arg,lab,ptn,active_arg,orbits_arg,options,stats_arg, - ws_arg,worksize,m_arg,n_arg,canong_arg) -graph *g_arg,*canong_arg; -set *active_arg,*ws_arg; -nvector *lab,*ptn,*orbits_arg; -int worksize,m_arg,n_arg; -optionblk *options; -statsblk *stats_arg; -{ - register int i; - int numcells; - - /* check for excessive sizes: */ - if (m_arg > MAXM) - { - stats_arg->errstatus = MTOOBIG; -#ifdef ERRFILE - fprintf(ERRFILE,"nauty: need m <= %d\n\n",MAXM); -#endif - return; - } - if (n_arg > MAXN || n_arg > WORDSIZE * m_arg) - { - stats_arg->errstatus = NTOOBIG; -#ifdef ERRFILE - fprintf(ERRFILE, - "nauty: need n <= min(%d,%d*m)\n\n",MAXM,WORDSIZE); -#endif - return; - } - - /* take copies of some args, and options: */ - m = m_arg; - n = n_arg; - g = g_arg; - orbits = orbits_arg; - stats = stats_arg; - - getcanon = options->getcanon; - digraph = options->digraph; - writeautoms = options->writeautoms; - domarkers = options->writemarkers; - cartesian = options->cartesian; - linelength = options->linelength; - if (digraph) - tc_level = 0; - else - tc_level = options->tc_level; - outfile = (options->outfile == (FILE*)NULL ? - stdout : options->outfile); - usernodeproc = options->usernodeproc; - userautomproc = options->userautomproc; - userlevelproc = options->userlevelproc; - - if (options->userrefproc == NILFUNCTION) - if (m == 1) - refproc = refine1; - else - refproc = refine; - else - refproc = options->userrefproc; - - if (options->usertcellproc == NILFUNCTION) - tcellproc = targetcell; - else - tcellproc = options->usertcellproc; - - invarproc = options->invarproc; - if (options->mininvarlevel < 0 && options->getcanon) - mininvarlevel = -options->mininvarlevel; - else - mininvarlevel = options->mininvarlevel; - if (options->maxinvarlevel < 0 && options->getcanon) - maxinvarlevel = -options->maxinvarlevel; - else - maxinvarlevel = options->maxinvarlevel; - invararg = options->invararg; - -// CHANGE added braces around if (getcanon) to eliminate compile warning - if (getcanon) - { - if (canong_arg == NILGRAPH) - { - stats_arg->errstatus = CANONGNIL; -#ifdef ERRFILE - fprintf(ERRFILE, - "nauty: canong=NILGRAPH but options.getcanon=TRUE\n\n"); -#endif - return; - } - else - canong = canong_arg; - } - - /* initialize everything: */ - if (options->defaultptn) - { - for (i = 0; i < n; ++i) /* give all verts same colour */ - { - lab[i] = i; - ptn[i] = INFINITY; - } - ptn[n-1] = 0; - EMPTYSET(active,m); - ADDELEMENT(active,0); - numcells = 1; - } - else - { - ptn[n-1] = 0; - numcells = 0; - for (i = 0; i < n; ++i) - if (ptn[i] != 0) - ptn[i] = INFINITY; - else - ++numcells; - if (active_arg == NILSET) - { - EMPTYSET(active,m); - for (i = 0; i < n; ++i) - { - ADDELEMENT(active,i); - while (ptn[i]) - ++i; - } - } - else - for (i = 0; i < M; ++i) - active[i] = active_arg[i]; - } - - for (i = 0; i < n; ++i) - orbits[i] = i; - stats->grpsize1 = 1.0; - stats->grpsize2 = 0; - stats->numgenerators = 0; - stats->numnodes = 0; - stats->numbadleaves = 0; - stats->tctotal = 0; - stats->canupdates = 0; - stats->numorbits = n; - EMPTYSET(fixedpts,m); - noncheaplevel = 1; - eqlev_canon = -1; /* needed even if !getcanon */ - - if (worksize >= 2 * m) - workspace = ws_arg; - else - { - workspace = defltwork; - worksize = 2 * MAXM; - } - worktop = workspace + (worksize - worksize % (2 * m)); - fmptr = workspace; - - /* here goes: */ - stats->errstatus = 0; - needshortprune = FALSE; - invarsuclevel = INFINITY; - invapplics = invsuccesses = 0; - - firstpathnode(lab,ptn,1,numcells); - - if (getcanon) - { - updatecan(g,canong,canonlab,samerows,M,n); - for (i = 0; i < n; ++i) - lab[i] = canonlab[i]; - } - stats->invarsuclevel = - (invarsuclevel == INFINITY ? 0 : invarsuclevel); - stats->invapplics = invapplics; - stats->invsuccesses = invsuccesses; -} - -/***************************************************************************** -* * -* firstpathnode(lab,ptn,level,numcells) produces a node on the leftmost * -* path down the tree. The parameters describe the level and the current * -* colour partition. The set of active cells is taken from the global set * -* 'active'. If the refined partition is not discrete, the leftmost child * -* is produced by calling firstpathnode, and the other children by calling * -* othernode. * -* The value returned is the level to return to. * -* * -* FUNCTIONS CALLED: (*usernodeproc)(),doref(),cheapautom(), * -* firstterminal(),nextelement(),breakout(), * -* firstpathnode(),othernode(),recover(),writestats(), * -* (*userlevelproc)(),(*tcellproc)(),shortprune() * -* * -*****************************************************************************/ - -static int -firstpathnode(lab,ptn,level,numcells) -nvector *lab,*ptn; -int level,numcells; -{ - register int tv; - // CHANGE assigned 0 to childcount to eliminate compile warning - int tv1,index,rtnlevel,tcellsize,tc,childcount=0,qinvar,refcode; - set tcell[MAXM]; - - ++stats->numnodes; - - /* refine partition : */ - doref(g,lab,ptn,level,&numcells,&qinvar,workperm, - active,&refcode,refproc,invarproc, - mininvarlevel,maxinvarlevel,invararg,digraph,M,n); - firstcode[level] = (short)refcode; - if (qinvar > 0) - { - ++invapplics; - if (qinvar == 2) - { - ++invsuccesses; - if (mininvarlevel < 0) - mininvarlevel = level; - if (maxinvarlevel < 0) - maxinvarlevel = level; - if (level < invarsuclevel) - invarsuclevel = level; - } - } - - tc = -1; - if (numcells != n) - { - /* locate new target cell, setting tc to its position in lab, tcell - to its contents, and tcellsize to its size: */ - (*tcellproc)(g,lab,ptn,level,numcells,tcell,&tcellsize, - &tc,tc_level,-1,M,n); - stats->tctotal += tcellsize; - } - firsttc[level] = tc; - - /* optionally call user-defined node examination procedure: */ - OPTCALL(usernodeproc) - (g,lab,ptn,level,numcells,tc,(int)firstcode[level],M,n); - - if (numcells == n) /* found first leaf? */ - { - firstterminal(lab,level); - OPTCALL(userlevelproc)(lab,ptn,level,orbits,stats,0,1,1,n,0,n); - return(level - 1); - } - - if (noncheaplevel >= level && !cheapautom(ptn,level,digraph,n)) - noncheaplevel = level + 1; - - /* use the elements of the target cell to produce the children: */ - index = 0; - for (tv1 = tv = nextelement(tcell,M,-1); tv >= 0; - tv = nextelement(tcell,M,tv)) - { - if (orbits[tv] == tv) /* ie, not equiv to previous child */ - { - breakout(lab,ptn,level + 1,tc,tv,active,M); - ADDELEMENT(fixedpts,tv); - cosetindex = tv; - if (tv == tv1) - { - rtnlevel = firstpathnode(lab,ptn,level + 1,numcells + 1); - childcount = 1; - gca_first = level; - stabvertex = tv1; - } - else - { - rtnlevel = othernode(lab,ptn,level + 1,numcells + 1); - ++childcount; - } - DELELEMENT(fixedpts,tv); - if (rtnlevel < level) - return(rtnlevel); - if (needshortprune) - { - needshortprune = FALSE; - shortprune(tcell,fmptr - M,M); - } - recover(ptn,level); - } - if (orbits[tv] == tv1) /* ie, in same orbit as tv1 */ - ++index; - } - MULTIPLY(stats->grpsize1,stats->grpsize2,index); - - if (tcellsize == index && allsamelevel == level + 1) - --allsamelevel; - - if (domarkers) - writemarker(level,tv1,index,tcellsize,stats->numorbits,numcells); - OPTCALL(userlevelproc)(lab,ptn,level,orbits,stats,tv1,index,tcellsize, - numcells,childcount,n); - return(level - 1); -} - -/***************************************************************************** -* * -* othernode(lab,ptn,level,numcells) produces a node other than an ancestor * -* of the first leaf. The parameters describe the level and the colour * -* partition. The list of active cells is found in the global set 'active'. * -* The value returned is the level to return to. * -* * -* FUNCTIONS CALLED: (*usernodeproc)(),doref(),refine(),recover(), * -* processnode(),cheapautom(),(*tcellproc)(),shortprune(), * -* nextelement(),breakout(),othernode(),longprune() * -* * -*****************************************************************************/ - -static int -othernode(lab,ptn,level,numcells) -nvector *lab,*ptn; -int level,numcells; -{ - register int tv; - set tcell[MAXM]; - int tv1,refcode,rtnlevel,tcellsize,tc,qinvar; - short code; - - ++stats->numnodes; - - /* refine partition : */ - doref(g,lab,ptn,level,&numcells,&qinvar,workperm,active, - &refcode,refproc,invarproc,mininvarlevel,maxinvarlevel, - invararg,digraph,M,n); - code = (short)refcode; - if (qinvar > 0) - { - ++invapplics; - if (qinvar == 2) - { - ++invsuccesses; - if (level < invarsuclevel) - invarsuclevel = level; - } - } - - if (eqlev_first == level - 1 && code == firstcode[level]) - eqlev_first = level; - if (getcanon) - { - if (eqlev_canon == level - 1) - { - if (code < canoncode[level]) - comp_canon = -1; - else if (code > canoncode[level]) - comp_canon = 1; - else - { - comp_canon = 0; - eqlev_canon = level; - } - } - if (comp_canon > 0) - canoncode[level] = code; - } - - tc = -1; - /* If children will be required, find new target cell and set tc to its - position in lab, tcell to its contents, and tcellsize to its size: */ - - if (numcells < n && (eqlev_first == level || - // CHANGE added parens around last && to eliminate compile warning - (getcanon && comp_canon >= 0))) - { - if (!getcanon || comp_canon < 0) - { - (*tcellproc)(g,lab,ptn,level,numcells,tcell,&tcellsize, - &tc,tc_level,firsttc[level],M,n); - if (tc != firsttc[level]) - eqlev_first = level - 1; - } - else - (*tcellproc)(g,lab,ptn,level,numcells,tcell,&tcellsize, - &tc,tc_level,-1,M,n); - stats->tctotal += tcellsize; - } - - /* optionally call user-defined node examination procedure: */ - OPTCALL(usernodeproc)(g,lab,ptn,level,numcells,tc,(int)code,M,n); - - /* call processnode to classify the type of this node: */ - - rtnlevel = processnode(lab,ptn,level,numcells); - if (rtnlevel < level) /* keep returning if necessary */ - return(rtnlevel); - if (needshortprune) - { - needshortprune = FALSE; - shortprune(tcell,fmptr - M,M); - } - - if (!cheapautom(ptn,level,digraph,n)) - noncheaplevel = level + 1; - - /* use the elements of the target cell to produce the children: */ - for (tv1 = tv = nextelement(tcell,M,-1); tv >= 0; - tv = nextelement(tcell,M,tv)) - { - breakout(lab,ptn,level + 1,tc,tv,active,M); - ADDELEMENT(fixedpts,tv); - rtnlevel = othernode(lab,ptn,level + 1,numcells + 1); - DELELEMENT(fixedpts,tv); - - if (rtnlevel < level) - return(rtnlevel); - /* use stored automorphism data to prune target cell: */ - if (needshortprune) - { - needshortprune = FALSE; - shortprune(tcell,fmptr - M,M); - } - if (tv == tv1) - longprune(tcell,fixedpts,workspace,fmptr,M); - - recover(ptn,level); - } - - return(level - 1); -} - -/***************************************************************************** -* * -* Process the first leaf of the tree. * -* * -* FUNCTIONS CALLED: NONE * -* * -*****************************************************************************/ - -static void -firstterminal(lab,level) -nvector *lab; -register int level; -{ - register int i; - - stats->maxlevel = level; - gca_first = allsamelevel = eqlev_first = level; - firstcode[level + 1] = INFINITY; - firsttc[level + 1] = -1; - - for (i = 0; i < n; ++i) - firstlab[i] = lab[i]; - - if (getcanon) - { - canonlevel = eqlev_canon = gca_canon = level; - comp_canon = 0; - samerows = 0; - for (i = 0; i < n; ++i) - canonlab[i] = lab[i]; - for (i = 0; i <= level; ++i) - canoncode[i] = firstcode[i]; - canoncode[level + 1] = INFINITY; - stats->canupdates = 1; - } -} - -/***************************************************************************** -* * -* Process a node other than the first leaf or its ancestors. It is first * -* classified into one of five types and then action is taken appropriate * -* to that type. The types are * -* * -* 0: Nothing unusual. This is just a node internal to the tree whose * -* children need to be generated sometime. * -* 1: This is a leaf equivalent to the first leaf. The mapping from * -* firstlab to lab is thus an automorphism. After processing the * -* automorphism, we can return all the way to the closest invocation * -* of firstpathnode. * -* 2: This is a leaf equivalent to the bsf leaf. Again, we have found an * -* automorphism, but it may or may not be as useful as one from a * -* type-1 node. Return as far up the tree as possible. * -* 3: This is a new bsf node, provably better than the previous bsf node. * -* After updating canonlab etc., treat it the same as type 4. * -* 4: This is a leaf for which we can prove that no descendant is * -* equivalent to the first or bsf leaf or better than the bsf leaf. * -* Return up the tree as far as possible, but this may only be by * -* one level. * -* * -* Types 2 and 3 can't occur if getcanon==FALSE. * -* The value returned is the level in the tree to return to, which can be * -* anywhere up to the closest invocation of firstpathnode. * -* * -* FUNCTIONS CALLED: isautom(),updatecan(),testcanlab(),fmperm(), * -* writeperm(),(*userautomproc)(),orbjoin(), * -* shortprune(),fmptn() * -* * -*****************************************************************************/ - -static int -processnode(lab,ptn,level,numcells) -nvector *lab,*ptn; -int level,numcells; -{ - register int i,code,save,newlevel; - boolean ispruneok; - int sr; - - code = 0; - if (eqlev_first != level && (!getcanon || comp_canon < 0)) - code = 4; - else if (numcells == n) - { - if (eqlev_first == level) - { - for (i = 0; i < n; ++i) - workperm[firstlab[i]] = lab[i]; - - if (gca_first >= noncheaplevel || - isautom(g,workperm,digraph,M,n)) - code = 1; - } - // CHANGE added braces on this if stmt to eliminate compile warning - if (code == 0) - { - if (getcanon) - { - sr = 0; - if (comp_canon == 0) - { - if (level < canonlevel) - comp_canon = 1; - else - { - updatecan(g,canong,canonlab, - samerows,M,n); - samerows = n; - comp_canon = testcanlab(g,canong,lab,&sr,M,n); - } - } - if (comp_canon == 0) - { - for (i = 0; i < n; ++i) - workperm[canonlab[i]] = lab[i]; - code = 2; - } - else if (comp_canon > 0) - code = 3; - else - code = 4; - } - else - code = 4; - } - } - - if (code != 0 && level > stats->maxlevel) - stats->maxlevel = level; - - switch (code) - { - case 0: /* nothing unusual noticed */ - return(level); - - case 1: /* lab is equivalent to firstlab */ - if (fmptr == worktop) - fmptr -= 2 * M; - fmperm(workperm,fmptr,fmptr + M,M,n); - fmptr += 2 * M; - if (writeautoms) - writeperm(outfile,workperm,cartesian,linelength,n); - stats->numorbits = orbjoin(orbits,workperm,n); - ++stats->numgenerators; - OPTCALL(userautomproc)(stats->numgenerators,workperm,orbits, - stats->numorbits,stabvertex,n); - return(gca_first); - - case 2: /* lab is equivalent to canonlab */ - if (fmptr == worktop) - fmptr -= 2 * M; - fmperm(workperm,fmptr,fmptr + M,M,n); - fmptr += 2 * M; - save = stats->numorbits; - stats->numorbits = orbjoin(orbits,workperm,n); - if (stats->numorbits == save) - { - if (gca_canon != gca_first) - needshortprune = TRUE; - return(gca_canon); - } - if (writeautoms) - writeperm(outfile,workperm,cartesian,linelength,n); - ++stats->numgenerators; - OPTCALL(userautomproc)(stats->numgenerators,workperm,orbits, - stats->numorbits,stabvertex,n); - if (orbits[cosetindex] < cosetindex) - return(gca_first); - if (gca_canon != gca_first) - needshortprune = TRUE; - return(gca_canon); - - case 3: /* lab is better than canonlab */ - ++stats->canupdates; - for (i = 0; i < n; ++i) - canonlab[i] = lab[i]; - canonlevel = eqlev_canon = gca_canon = level; - comp_canon = 0; - canoncode[level + 1] = INFINITY; - samerows = sr; - break; - - case 4: /* non-automorphism terminal node */ - ++stats->numbadleaves; - break; - } /* end of switch statement */ - - /* only cases 3 and 4 get this far: */ - if (level != noncheaplevel) - { - ispruneok = TRUE; - if (fmptr == worktop) - fmptr -= 2 * M; - fmptn(lab,ptn,noncheaplevel,fmptr,fmptr + M,M,n); - fmptr += 2 * M; - } - else - ispruneok = FALSE; - - save = (allsamelevel > eqlev_canon ? allsamelevel - 1 : eqlev_canon); - newlevel = (noncheaplevel <= save ? noncheaplevel - 1 : save); - - if (ispruneok && newlevel != gca_first) - needshortprune = TRUE; - return(newlevel); - } - -/***************************************************************************** -* * -* Recover the partition nest at level 'level' and update various other * -* parameters. * -* * -* FUNCTIONS CALLED: NONE * -* * -*****************************************************************************/ - -static void -recover(ptn,level) -register nvector *ptn; -register int level; -{ - register int i; - - for (i = 0; i < n; ++i) - if (ptn[i] > level) - ptn[i] = INFINITY; - - if (level < noncheaplevel) - noncheaplevel = level + 1; - if (level < eqlev_first) - eqlev_first = level; - if (getcanon) - { - if (level < gca_canon) - gca_canon = level; - if (level <= eqlev_canon) - { - eqlev_canon = level; - comp_canon = 0; - } - } -} - -/***************************************************************************** -* * -* Write statistics concerning an ancestor of the first leaf. * -* * -* level = its level * -* tv = the vertex fixed to get the first child = the smallest-numbered * -* vertex in the target cell * -* cellsize = the size of the target cell * -* index = the number of vertices in the target cell which were equivalent * -* to tv = the index of the stabiliser of tv in the group * -* fixing the colour partition at this level * -* * -* numorbits = the number of orbits of the group generated by all the * -* automorphisms so far discovered * -* * -* numcells = the total number of cells in the equitable partition at this * -* level * -* * -* FUNCTIONS CALLED: itos(),putstring() * -* * -*****************************************************************************/ - -static void -writemarker(level,tv,index,tcellsize,numorbits,numcells) -int level,tv,index,tcellsize,numorbits,numcells; -{ - char s[30]; - -#define PUTINT(i) itos(i,s); putstring(outfile,s) -#define PUTSTR(x) putstring(outfile,x) - - PUTSTR("level "); - PUTINT(level); - PUTSTR(": "); - if (numcells != numorbits) - { - PUTINT(numcells); - PUTSTR(" cell"); - if (numcells == 1) - PUTSTR("; "); - else - PUTSTR("s; "); - } - PUTINT(numorbits); - PUTSTR(" orbit"); - if (numorbits == 1) - PUTSTR("; "); - else - PUTSTR("s; "); - PUTINT(tv + labelorg); - PUTSTR(" fixed; index "); - PUTINT(index); - if (tcellsize != index) - { - PUTSTR("/"); - PUTINT(tcellsize); - } - PUTSTR("\n"); -} - -/***************************************************************************** -* * -* nauty_null() does nothing. See dreadnaut.c for its purpose. * -* * -*****************************************************************************/ - -void -nauty_null() -{ -} diff --git a/planarity_src/c/nauty/nauty.h b/planarity_src/c/nauty/nauty.h deleted file mode 100644 index 1dfae19..0000000 --- a/planarity_src/c/nauty/nauty.h +++ /dev/null @@ -1,721 +0,0 @@ -/***************************************************************************** - * This is the header file for Version 1.9+ of nauty(). * - *****************************************************************************/ - -#ifndef NAUTYH_READ /* only process this file once */ - -#include -// CHANGE start -// Adding this to remove warnings about needing declaration for exit() -#include -// CHANGE end - -/* Exactly one of the symbols with names starting with "SYS_" should - have the value 1. All the others should have the value 0. */ - -#define SYS_VAXBSD 0 /* older BSD unix 4.2 on a VAX */ -#define SYS_CRAY 0 /* Cray UNIX, portable or standard C */ -#define SYS_APOLLO 0 /* DOMAIN C on Apollo */ -#define SYS_BSDUNIX 1 /* other BSD unix (cc or gcc compilers) */ -#define SYS_UNIX 0 /* miscellaneous non-BSD unix, including A/UX */ -#define SYS_ALPHA 0 /* Alpha UNIX */ -#define SYS_VAXVMS 0 /* VAX11C or GCC on a VAX under VMS */ -#define SYS_MACLSC 0 /* Lightspeed C on an Apple Macintosh (Version 1) */ -#define SYS_MACTHINK 0 /* THINK C on an Apple Macintosh (Version 4) */ -#define SYS_MACAZT 0 /* Aztec C on an Apple Macintosh */ -#define SYS_MACMPW 0 /* Apple Macintosh Workshop C */ -#define SYS_AMIGALC 0 /* Lattice C on a Commodore Amiga */ -#define SYS_AMIGAAZT 0 /* Aztec C on a Commodore Amiga */ -#define SYS_PCMS4 0 /* Microsoft C 4.0 on IBM PC */ -#define SYS_PCMS5 0 /* Microsoft C 5.1 or Quick C 2.5 on IBM PC */ -#define SYS_PCTURBO 0 /* Turbo C on IBM PC */ -#define SYS_IBMC 0 /* IBM C Set/2 under OS/2 */ -#define SYS_MISC 0 /* anything else */ - -#if SYS_MISC -#define CPUDEFS -#define CPUTIME 0 -#endif - -/***************************************************************************** -* * -* AUTHOR: Brendan D. McKay * -* Computer Science Department, Australian National University, * -* GPO Box 4, Canberra, Australia 0200 * -* phone: +61 6 249 3845 fax: +61 6 249 0010 * -* email: bdm@cs.anu.edu.au * -* * -* Copyright (1984-1993) Brendan McKay. All rights reserved. Permission * -* is hereby given for use and/or distribution with the exception of * -* sale for profit or application with nontrivial military significance. * -* You must not remove this copyright notice, and you must document any * -* changes that you make to this program. * -* This software is subject to this copyright only, irrespective of * -* any copyright attached to any package of which this is a part. * -* * -* This program is only provided "as is". No responsibility will be taken * -* by the author, his employer or his pet rabbit* for any misfortune which * -* befalls you because of its use. I don't think it will delete all your * -* files, burn down your computer room or turn your children against you, * -* but if it does: stiff cheddar. On the other hand, I very much welcome * -* bug reports, or at least I would if there were any bugs. * -* * RIP, 1989 * -* * -* If you wish to acknowledge use of this program in published articles, * -* please do so by citing the User's Guide: * -* * -* B. D. McKay, nauty User's Guide (Version 1.5), Technical Report * -* TR-CS-90-02, Australian National University, Department of * -* Computer Science, 1990. * -* * -* CHANGE HISTORY * -* 10-Nov-87 : final changes for version 1.2 * -* 5-Dec-87 : renamed to version 1.3 (no changes to this file) * -* 28-Sep-88 : added PC Turbo C support, making version 1.4 * -* 23-Mar-89 : changes for version 1.5 : * -* - reworked M==1 code * -* - defined NAUTYVERSION string * -* - made NAUTYH_READ to allow this file to be read twice * -* - added optional ANSI function prototypes * -* - added validity check for WORDSIZE * -* - added new fields to optionblk structure * -* - updated DEFAULTOPTIONS to add invariants fields * -* - added (set*) cast to definition of GRAPHROW * -* - added definition of ALLOCS and FREES * -* 25-Mar-89 : - added declaration of new function doref() * -* - added UNION macro * -* 29-Mar-89 : - reduced the default MAXN for small machines * -* - removed OUTOFSPACE (no longer used) * -* - added SETDIFF and XOR macros * -* 2-Apr-89 : - extended statsblk structure * -* 4-Apr-89 : - added IS_* macros * -* - added ERRFILE definition * -* - replaced statsblk.outofspace by statsblk.errstatus * -* 5-Apr-89 : - deleted definition of np2vector (no longer used) * -* - introduced EMPTYSET macro * -* 12-Apr-89 : - eliminated MARK, UNMARK and ISMARKED (no longer used) * -* 18-Apr-89 : - added MTOOBIG and CANONGNIL * -* 12-May-89 : - made ISELEM1 and ISELEMENT return 0 or 1 * -* 2-Mar-90 : - added EXTPROC macro and used it * -* 12-Mar-90 : - added SYS_CRAY, with help from N. Sloane and A. Grosky * -* - added dummy groupopts field to optionblk * -* - select some ANSI things if __STDC__ exists * -* 20-Mar-90 : - changed default MAXN for Macintosh versions * -* - created SYS_MACTHINK for Macintosh THINK compiler * -* 27-Mar-90 : - split SYS_MSDOS into SYS_PCMS4 and SYS_PCMS5 * -* 13-Oct-90 : changes for version 1.6: * -* - fix definition of setword for WORDSIZE==64 * -* 14-Oct-90 : - added SYS_APOLLO version to avoid compiler bug * -* 15-Oct-90 : - improve detection of ANSI conformance * -* 17-Oct-90 : - changed temp name in EMPTYSET to avoid A/UX bug * -* 16-Apr-91 : changes for version 1.7: * -* - made version SYS_PCTURBO use free(), not cfree() * -* 2-Sep-91 : - noted that SYS_PCMS5 also works for Quick C * -* - moved MULTIPLY to here from nauty.c * -* 12-Jun-92 : - changed the top part of this comment * -* 27-Aug-92 : - added version SYS_IBMC, thanks to Ivo Duentsch * -* 5-Jun-93 : - renamed to version 1.7+, only change in naututil.h * -* 29-Jul-93 : changes for version 1.8: * -* - fixed error in default 64-bit version of FIRSTBIT * -* (not used in any version before ALPHA) * -* - installed ALPHA version (thanks to Gordon Royle) * -* - defined ALLOCS,FREES for SYS_IBMC * -* 3-Sep-93 : - make calloc void* in ALPHA version * -* 17-Sep-93 : - renamed to version 1.9, * -* changed only dreadnaut.c and nautinv.c * -* 24-Feb-94 : changes for version 1.10: * -* - added version SYS_AMIGAAZT, thanks to Carsten Saager * -* (making 1.9+) * -* 19-Apr-95 : - added prototype wrapper for C++, * -* thanks to Daniel Huson * -* * -*****************************************************************************/ - -#define IS_UNIX (SYS_VAXBSD | SYS_BSDUNIX | SYS_CRAY | \ - SYS_UNIX | SYS_APOLLO | SYS_ALPHA) -#define IS_MAC (SYS_MACMPW | SYS_MACAZT | SYS_MACLSC | SYS_MACTHINK) -#define IS_PC (SYS_PCTURBO | SYS_PCMS4 | SYS_PCMS5) -#define IS_AMIGA (SYS_AMIGALC | SYS_AMIGAAZT) - -/***************************************************************************** -* * -* 16-bit, 32-bit and 64-bit versions can be selected by defining WORDSIZE. * -* The largest graph that can be handled has MAXN vertices. * -* Both WORDSIZE and MAXN can be defined on the command line. * -* WORDSIZE must be 16, 32 or 64; MAXN must be <= INFINITY - 2; * -* If only very small graphs need to be processed, use MAXN<=WORDSIZE * -* since this causes substantial code optimizations. * -* The symbol EXTDEFS should be defined (with arbitrary value) before this * -* file is included, except for one of each group of files linked together. * -* The file containing the main program is suggested. This ensures that * -* arrays bit, bytecount and leftbit, and parameter labelorg, are only * -* initialised once, which some linkers think is necessary. * -* * -* Conventions and Assumptions: * -* * -* A 'setword' is the chunk of memory that is occupied by one part of * -* a set. This is assumed to be >= WORDSIZE bits in size. * -* * -* The rightmost (loworder) WORDSIZE bits of setwords are numbered * -* 0..WORDSIZE-1, left to right. It is necessary that the 2^WORDSIZE * -* setwords with the other bits zero are totally ordered under <,=,>. * -* This needs care on a 1's-complement machine. * -* * -* The int variables m and n have consistent meanings throughout. * -* Graphs have n vertices always, and sets have m setwords always. * -* * -* A 'set' consists of m contiguous setwords, whose bits are numbered * -* 0,1,2,... from left (high-order) to right (low-order), using only * -* the rightmost WORDSIZE bits of each setword. It is used to * -* represent a subset of {0,1,...,n-1} in the usual way - bit number x * -* is 1 iff x is in the subset. Bits numbered n or greater, and * -* unnumbered bits, are assumed permanently zero. * -* * -* A 'graph' consists of n contiguous sets. The i-th set represents * -* the vertices adjacent to vertex i, for i = 0,1,...,n-1. * -* * -* A 'permutation' is an array of n short ints, repesenting a * -* permutation of the set {0,1,...,n-1}. The value of the i-th entry * -* is the number to which i is mapped. * -* * -* If g is a graph and p is a permutation, then g^p is the graph in * -* which vertex i is adjacent to vertex j iff vertex p[i] is adjacent * -* to vertex p[j] in g. * -* * -* An 'nvector' is any array of n ints. Actually, nvector==int. * -* * -* A partition nest is represented by a pair (lab,ptn), where lab and ptn * -* are nvectors. The "partition at level x" is the partition whose cells * -* are {lab[i],lab[i+1],...,lab[j]}, where [i,j] is a maximal subinterval * -* of [0,n-1] such that ptn[k] > x for i <= k < j and ptn[j] <= x. * -* The partition at level 0 is given to nauty by the user. This is * -* refined for the root of the tree, which has level 1. * -* * -*****************************************************************************/ - -#if SYS_VAXBSD -#define NAUTYVERSION "1.9+ (VAXBSD)" -#endif -#if SYS_BSDUNIX -#define NAUTYVERSION "1.9+ (BSDUNIX)" -#endif -#if SYS_UNIX -#define NAUTYVERSION "1.9+ (UNIX)" -#endif -#if SYS_APOLLO -#define NAUTYVERSION "1.9+ (APOLLO)" -#endif -#if SYS_VAXVMS -#define NAUTYVERSION "1.9+ (VAXVMS)" -#endif -#if SYS_MACLSC -#define NAUTYVERSION "1.9+ (MACLSC)" -#endif -#if SYS_MACTHINK -#define NAUTYVERSION "1.9+ (MACTHINK)" -#endif -#if SYS_MACAZT -#define NAUTYVERSION "1.9+ (MACAZT)" -#endif -#if SYS_MACMPW -#define NAUTYVERSION "1.9+ (MACMPW)" -#endif -#if SYS_AMIGALC -#define NAUTYVERSION "1.9+ (AMIGALC)" -#endif -#if SYS_AMIGAAZT -#define NAUTYVERSION "1.9+ (AMIGAAZT)" -#endif -#if SYS_PCMS4 -#define NAUTYVERSION "1.9+ (PCMS4)" -#endif -#if SYS_PCMS5 -#define NAUTYVERSION "1.9+ (PCMS5)" -#endif -#if SYS_PCTURBO -#define NAUTYVERSION "1.9+ (PCTURBO)" -#endif -#if SYS_CRAY -#define NAUTYVERSION "1.9+ (CRAY)" -#endif -#if SYS_IBMC -#define NAUTYVERSION "1.9+ (IBM C Set/2 for OS/2)" -#endif -#if SYS_ALPHA -#define NAUTYVERSION "1.9+ (Alpha)" -#endif -#if SYS_MISC -#define NAUTYVERSION "1.9+ (MISC)" -#endif - -#ifdef WORDSIZE - -#if ((WORDSIZE != 16) & (WORDSIZE != 32) & (WORDSIZE != 64)) -ERROR: WORDSIZE must be 16, 32 or 64 -#endif - -#else /* WORDSIZE undefined */ - -#if IS_PC -#define WORDSIZE 16 /* number of set elements per setword (16 or 32) */ -#else -#if (SYS_CRAY | SYS_ALPHA) -#define WORDSIZE 64 -#else -#define WORDSIZE 32 -#endif -#endif - -#endif /* WORDSIZE */ - -#ifndef MAXN /* maximum allowed n value */ - /* can be defined outside */ -#if (IS_AMIGA | SYS_MISC | SYS_PCMS4 | SYS_PCMS5 | SYS_PCTURBO) -#define MAXN 448 -#endif -#if (SYS_MACLSC | SYS_MACAZT | SYS_MACMPW) -#define MAXN 480 -#endif -#if SYS_MACTHINK -#define MAXN 640 -#endif -#if (SYS_UNIX | SYS_BSDUNIX | SYS_VAXBSD | SYS_APOLLO | SYS_IBMC) -#define MAXN 1024 -#endif -#if (SYS_VAXVMS | SYS_CRAY | SYS_ALPHA) -#define MAXN 2048 -#endif - -#endif /* MAXN */ - -#define MAXM ((MAXN+WORDSIZE-1)/WORDSIZE) /* max setwords in a set */ - - /* set operations (setadd is its address, pos is the bit number): */ -#if MAXM==1 - -#define SETWD(pos) 0 -#define SETBT(pos) (pos) - -#else /* MAXM > 1 */ - -#if WORDSIZE==64 -#define SETWD(pos) ((pos)>>6) /* number of setword containing bit pos */ -#define SETBT(pos) ((pos)&077) /* position within setword of bit pos */ -#else -#if WORDSIZE==32 -#define SETWD(pos) ((pos)>>5) -#define SETBT(pos) ((pos)&037) -#else /* WORDSIZE==16 */ -#define SETWD(pos) ((pos)>>4) -#define SETBT(pos) ((pos)&017) -#endif -#endif - -#endif /* MAXM */ - -#if WORDSIZE==64 -#define TIMESWORDSIZE(w) ((w)<<6) /* w*WORDSIZE */ -#else -#if WORDSIZE==32 -#define TIMESWORDSIZE(w) ((w)<<5) -#else /* WORDSIZE==16 */ -#define TIMESWORDSIZE(w) ((w)<<4) -#endif -#endif - -#define ADDELEM1(setadd,pos) (*(setadd) |= bit[pos]) -#define DELELEM1(setadd,pos) (*(setadd) &= ~bit[pos]) -#define ISELEM1(setadd,pos) ((*(setadd) & bit[pos]) != 0) - -#define ADDELEMENT(setadd,pos) ((setadd)[SETWD(pos)] |= bit[SETBT(pos)]) -#define DELELEMENT(setadd,pos) ((setadd)[SETWD(pos)] &= ~bit[SETBT(pos)]) -#define ISELEMENT(setadd,pos) (((setadd)[SETWD(pos)] & bit[SETBT(pos)]) != 0) - -#if MAXM==1 /* initialise set to empty */ -#define EMPTYSET(setadd,m) *(setadd) = 0; -#else -#define EMPTYSET(setadd,m) \ - {register setword *es; \ - for (es = (setword*)(setadd)+(m); --es >= (setword*)(setadd);) *es=0;} -#endif - -#if MAXM==1 /* obsolete version of EMPTYSET */ -#define MAKEEMPTY(setadd,m,i) *(setadd) = 0 -#else -#define MAKEEMPTY(setadd,m,i) for(i=m;--i>=0;)setadd[i]=0 -#endif - -#define NOTSUBSET(word1,word2) ((word1) & ~(word2)) /* test if the 1-bits - in setword word1 do not form a subset of those in word2 */ -#define INTERSECT(word1,word2) ((word1) &= (word2)) /* AND word2 into word1 */ -#define UNION(word1,word2) ((word1) |= (word2)) /* OR word2 into word1 */ -#define SETDIFF(word1,word2) ((word1) &= ~(word2)) /* - word2 into word1 */ -#define XOR(word1,word2) ((word1) ^= (word2)) /* XOR word2 into word1 */ -#define ZAPBIT(word,x) ((word) &= ~bit[x]) /* delete bit x in setword */ - -#if WORDSIZE==64 -#if SYS_CRAY -#define POPCOUNT(x) _popcnt(x) -#define FIRSTBIT(x) _leadz(x) -#define BITMASK(x) _mask(65+(x)) -#else -#define POPCOUNT(x) (bytecount[(x)>>56 & 0377] + bytecount[(x)>>48 & 0377] \ - + bytecount[(x)>>40 & 0377] + bytecount[(x)>>32 & 0377] \ - + bytecount[(x)>>24 & 0377] + bytecount[(x)>>16 & 0377] \ - + bytecount[(x)>>8 & 0377] + bytecount[(x) & 0377]) - /* number of 1-bits in a setword */ -#define FIRSTBIT(x) ((x) & 01777777777740000000000 ? \ - (x) & 01777770000000000000000 ? \ - (x) & 01774000000000000000000 ? \ - 0+leftbit[((x)>>56) & 0377] : \ - 8+leftbit[(x)>>48] \ - : (x) & 07760000000000000 ? \ - 16+leftbit[(x)>>40] : \ - 24+leftbit[(x)>>32] \ - : (x) & 037777600000 ? \ - (x) & 037700000000 ? \ - 32+leftbit[(x)>>24] : \ - 40+leftbit[(x)>>16] \ - : (x) & 0177400 ? \ - 48+leftbit[(x)>>8] : \ - 56+leftbit[x]) - /* get number of first 1-bit in non-zero setword (0..WORDSIZE-1) */ -#define BITMASK(x) (0777777777777777777777 >> (x)) /* setword whose rightmost - WORDSIZE-x-1 (numbered) bits are 1 and the rest 0 (0 <= x < WORDSIZE) */ -#endif /* CRAY */ -#else -#if WORDSIZE==32 -#define POPCOUNT(x) (bytecount[(x)>>24 & 0377] + bytecount[(x)>>16 & 0377] \ - + bytecount[(x)>>8 & 0377] + bytecount[(x) & 0377]) -#define FIRSTBIT(x) ((x) & 037777600000 ? ((x) & 037700000000 ? \ - leftbit[((x)>>24) & 0377] : 8+leftbit[(x)>>16]) \ - : ((x) & 0177400 ? 16+leftbit[(x)>>8] : 24+leftbit[x])) -#define BITMASK(x) (017777777777 >> (x)) /* setword whose rightmost - WORDSIZE-x-1 (numbered) bits are 1 and the rest 0 (0 <= x < WORDSIZE) */ -#else /* WORDSIZE==16 */ -#define POPCOUNT(x) (bytecount[(x)>>8 & 0377] + bytecount[(x) & 0377]) -#define FIRSTBIT(x) ((x) & 0177400 ? leftbit[((x)>>8) & 0377] : 8+leftbit[x]) -#define BITMASK(x) (077777 >> (x)) -#endif -#endif - -#if MAXM==1 -#define GRAPHROW(g,v,m) ((set*)(g) + (v)) -#else -#define GRAPHROW(g,v,m) ((set*)(g) + (long)(v) * (long)(m)) - /* address of row v of graph g. v and m are ints. Beware of v*m being - too large for an int. */ -#endif - - /* various constants: */ -#define FALSE 0 -#define TRUE 1 -#define INFINITY 077777 /* positive short int greater than MAXN+2 */ - - /* typedefs for sets, graphs, permutations, etc.: */ -typedef int boolean; /* boolean MUST be the same as int */ - -#if WORDSIZE>16 -typedef unsigned long setword; -#else -typedef unsigned short setword; -#endif - -#if SYS_VAXBSD -#define UPROC int /* avoids compiler bug in BSD 4.2 on VAX */ -#else -#define UPROC void /* type of user-defined procedures */ -#endif - -typedef setword set,graph; -typedef int nvector,np2vector; -typedef short permutation; - -typedef struct -{ - int group_xyz; /* dummy field to make it non-empty */ -} groupblk; - -typedef struct -{ - boolean getcanon; /* make canong and canonlab? */ - boolean digraph; /* multiple edges or loops? */ - boolean writeautoms; /* write automorphisms? */ - boolean writemarkers; /* write stats on pts fixed, etc.? */ - boolean defaultptn; /* set lab,ptn,active for single cell? */ - boolean cartesian; /* use cartesian rep for writing automs? */ - int linelength; /* max chars/line (excl. '\n') for output */ - FILE *outfile; /* file for output, if any */ - UPROC (*userrefproc)(); /* replacement for usual refine procedure */ - UPROC (*userautomproc)(); /* procedure called for each automorphism */ - UPROC (*userlevelproc)(); /* procedure called for each level */ - UPROC (*usernodeproc)(); /* procedure called for each node */ - UPROC (*usertcellproc)(); /* replacement for targetcell procedure */ - UPROC (*invarproc)(); /* procedure to compute vertex-invariant */ - int tc_level; /* max level for smart target cell choosing */ - int mininvarlevel; /* min level for invariant computation */ - int maxinvarlevel; /* max level for invariant computation */ - int invararg; /* value passed to (*invarproc)() */ - groupblk *groupopts; /* placeholder for future group options */ -} optionblk; - -#if (SYS_MACAZT | SYS_MACMPW) -#define CONSOLWIDTH 72 -#else -#if IS_AMIGA -#define CONSOLWIDTH 75 -#else -#define CONSOLWIDTH 78 -#endif -#endif - -#define NILFUNCTION ((UPROC(*)())NULL) /* nil pointer to user-function */ -#define NILSET ((set*)NULL) /* nil pointer to set */ -#define NILGRAPH ((graph*)NULL) /* nil pointer to graph */ - -#define DEFAULTOPTIONS(options) optionblk options = {FALSE,FALSE,TRUE,TRUE,\ - TRUE,FALSE,CONSOLWIDTH,(FILE*)NULL,NILFUNCTION,NILFUNCTION,NILFUNCTION,\ - NILFUNCTION,NILFUNCTION,NILFUNCTION,0,0,0,0,(groupblk*)NULL} - -#if IS_AMIGA -#define PUTC(c,f) fputc(c,f) /* best way to write character c to file f */ -#else -#define PUTC(c,f) putc(c,f) -#endif - -typedef struct -{ - double grpsize1; /* size of group is */ - int grpsize2; /* grpsize1 * 10^grpsize2 */ -#if !SYS_MACAZT -#define groupsize1 grpsize1 /* for backwards compatibility */ -#define groupsize2 grpsize2 -#endif - int numorbits; /* number of orbits in group */ - int numgenerators; /* number of generators found */ - int errstatus; /* if non-zero : an error code */ -#define outofspace errstatus; /* for backwards compatibility */ - long numnodes; /* total number of nodes */ - long numbadleaves; /* number of leaves of no use */ - int maxlevel; /* maximum depth of search */ - long tctotal; /* total size of all target cells */ - long canupdates; /* number of updates of best label */ - long invapplics; /* number of applications of invarproc */ - long invsuccesses; /* number of successful applics of invarproc() */ - int invarsuclevel; /* least level where invarproc worked */ -} statsblk; - -/* codes for errstatus field: */ -#define NTOOBIG 1 /* n > MAXN or n > WORDSIZE*m */ -#define MTOOBIG 2 /* m > MAXM */ -#define CANONGNIL 3 /* canong = NILGRAPH, but getcanon = TRUE */ - -/* manipulation of real approximation to group size */ -#define MULTIPLY(s1,s2,i) if ((s1 *= i) >= 1e10) {s1 /= 1e10; s2 += 10;} - -#define ANSI_STDC 0 -#ifdef __STDC__ -#if __STDC__ == 1 -#undef ANSI_STDC -#define ANSI_STDC 1 -#endif -#endif - -/* The dynamic memory macros below are currently (version 1.9+) not used by - nauty, only by dreadnaut. They are defined in this file, though, because - their use by a future version of nauty is highly likely. - ALLOCS(x,y) should return a pointer (any pointer type) to x*y units of new - storage, not necessarily initialised. A "unit" of storage is defined by - the sizeof operator. x and y are int values, but x*y may well be too - large for an int. On failure, ALLOCS(x,y) should return a NULL pointer. - FREES(p) should free (or pretend to free) storage previously allocated by - ALLOCS, where p is the value that ALLOCS returned. */ - -#if SYS_MACAZT -extern char *NewPtr(); -extern void DisposPtr(); -#define ALLOCS(x,y) NewPtr((long)(x) * (long)(y)) -#define FREES(p) DisposPtr(p) -#endif - -#if (IS_UNIX | SYS_PCTURBO | SYS_PCMS4 | SYS_VAXVMS \ - | SYS_MACLSC | SYS_MISC | SYS_IBMC) -#if ANSI_STDC -#if SYS_APOLLO -#include /* Domain C has no stddef.h */ -#else -#include -#endif /* SYS_APOLLO */ -extern void *calloc(size_t,size_t); -extern void free(void*); -#define ALLOCS(x,y) calloc(x,y) -#define FREES(p) free(p) -#else /* not ANSI_STDC */ -#if SYS_ALPHA -extern void *calloc(); -#else -extern char *calloc(); -#endif /* SYS_ALPHA */ -extern void cfree(); -#define ALLOCS(x,y) calloc(x,y) -#if (SYS_PCTURBO | SYS_IBMC) -#define FREES(p) free(p) -#else -#define FREES(p) cfree(p) -#endif /* SYS_PCTURBO | SYS_IBMC */ -#endif /* ANSI_STDC */ -#endif /* IS_UNIX ... */ - -#if SYS_PCMS5 -#include -#define ALLOCS(x,y) calloc(x,y) -#define FREES(p) free(p) -#endif - -#if SYS_MACTHINK -#include -extern void *calloc(size_t,size_t); -extern void free(void*); -#define ALLOCS(x,y) calloc(x,y) -#define FREES(p) free(p) -#endif - -#if (SYS_MACMPW | IS_AMIGA) -extern char *calloc(); -extern void free(); -#define ALLOCS(x,y) calloc(x,y) -#define FREES(p) free(p) -#endif - -/* File to write error messages to (used as first argument to fprintf()). - Leave it undefined to get no error messages. dreadnaut uses this too. */ -#define ERRFILE stderr - -#ifdef EXTDEFS -extern setword bit[]; -extern int bytecount[]; -extern int leftbit[]; -extern int labelorg; -#else - /* array giving setwords with single 1-bit */ -#if WORDSIZE==64 -setword bit[] = {01000000000000000000000,0400000000000000000000, - 0200000000000000000000,0100000000000000000000, - 040000000000000000000,020000000000000000000, - 010000000000000000000,04000000000000000000, - 02000000000000000000,01000000000000000000, - 0400000000000000000,0200000000000000000, - 0100000000000000000,040000000000000000,020000000000000000, - 010000000000000000,04000000000000000,02000000000000000, - 01000000000000000,0400000000000000,0200000000000000, - 0100000000000000,040000000000000,020000000000000, - 010000000000000,04000000000000,02000000000000, - 01000000000000,0400000000000,0200000000000,0100000000000, - 040000000000,020000000000,010000000000,04000000000, - 02000000000,01000000000,0400000000,0200000000,0100000000, - 040000000,020000000,010000000,04000000,02000000,01000000, - 0400000,0200000,0100000,040000,020000,010000,04000, - 02000,01000,0400,0200,0100,040,020,010,04,02,01}; -#else -#if WORDSIZE==32 -setword bit[] = {020000000000,010000000000,04000000000,02000000000, - 01000000000,0400000000,0200000000,0100000000,040000000, - 020000000,010000000,04000000,02000000,01000000,0400000, - 0200000,0100000,040000,020000,010000,04000,02000,01000, - 0400,0200,0100,040,020,010,04,02,01}; -#else /* WORDSIZE==16 */ -setword bit[] = {0100000,040000,020000,010000,04000,02000,01000,0400,0200, - 0100,040,020,010,04,02,01}; -#endif -#endif - - /* array giving number of 1-bits in bytes valued 0..255: */ -int bytecount[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, - 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, - 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, - 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, - 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, - 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; - - /* array giving position (1..7) of high-order 1-bit in byte: */ -int leftbit[] = {8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; -int labelorg = 0; /* number of least-numbered vertex */ -#endif /* EXTDEFS */ - -#if ANSI_STDC -#ifndef ANSIPROT -#define ANSIPROT 1 -#endif -#endif - -#ifdef ANSIPROT -#define EXTPROC(func,args) extern func args; -#else -#define EXTPROC(func,args) extern func(); -#endif - -/* The following is for C++ programs that read nauty.h. Compile nauty - itself using C, not C++. */ - -#ifdef __cplusplus -extern "C" { -#endif - -EXTPROC(int bestcell,(graph*,nvector*,nvector*,int,int,int,int)) -EXTPROC(void breakout,(nvector*,nvector*,int,int,int,set*,int)) -EXTPROC(boolean cheapautom,(nvector*,int,boolean,int)) -EXTPROC(void doref,(graph*,nvector*,nvector*,int,int*,int*,permutation*,set*, - int*,UPROC(*)(),UPROC(*)(),int,int,int,boolean,int,int)) -EXTPROC(boolean isautom,(graph*,permutation*,boolean,int,int)) -EXTPROC(int itos,(int,char*)) -EXTPROC(void fmperm,(permutation*,set*,set*,int,int)) -EXTPROC(void fmptn,(nvector*,nvector*,int,set*,set*,int,int)) -EXTPROC(void longprune,(set*,set*,set*,set*,int)) -EXTPROC(void nauty,(graph*,nvector*,nvector*,set*,nvector*,optionblk*, - statsblk*,set*,int,int,int,graph*)) -EXTPROC(int nextelement,(set*,int,int)) -EXTPROC(int orbjoin,(nvector*,permutation*,int)) -EXTPROC(void permset,(set*,set*,int,permutation*)) -EXTPROC(void putstring,(FILE*,char*)) -EXTPROC(UPROC refine,(graph*,nvector*,nvector*,int,int*, - permutation*,set*,int*,int,int)) -EXTPROC(UPROC refine1,(graph*,nvector*,nvector*,int,int*, - permutation*,set*,int*,int,int)) -EXTPROC(void shortprune,(set*,set*,int)) -EXTPROC(UPROC targetcell,(graph*,nvector*,nvector*,int,int,set*,int*, - int*,int,int,int,int)) -EXTPROC(int testcanlab,(graph*,graph*,nvector*,int*,int,int)) -EXTPROC(void updatecan,(graph*,graph*,permutation*,int,int,int)) -EXTPROC(void writeperm,(FILE*,permutation*,boolean,int,int)) - -#ifdef __cplusplus -} -#endif - -#define NAUTYH_READ 1 -#endif /* NAUTYH_READ */ diff --git a/planarity_src/c/nauty/outproc.c b/planarity_src/c/nauty/outproc.c deleted file mode 100644 index 18f5143..0000000 --- a/planarity_src/c/nauty/outproc.c +++ /dev/null @@ -1,501 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2010, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -* 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. - -* 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. - -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 Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -/*********************************************************************** - This file contains functions that connect McKay's makeg program output - with planarity-related graph algorithm implementations. - ***********************************************************************/ - - -#define EXTDEFS -#define MAXN 16 -#include "naututil.h" -extern int g_maxn, g_mine, g_maxe, g_mod, g_res; -extern char g_command; -extern FILE *g_msgfile; -extern char quietMode; - -#include - -#include "outproc.h" -#include "testFramework.h" -#include "../graphColorVertices.h" - -int runTest(FILE *, char); - -testResultFrameworkP testFramework = NULL; -int errorFound = 0; - -int unittestMode = 0; - -/*********************************************************************** - WriteMatrixGraph() - - Writes a graph, giving n in decimal then the adj. matrix in hexadecimal - ***********************************************************************/ - -void WriteMatrixGraph(char *filename, graph *g, int n) -{ - FILE *Outfile = fopen(filename, "wt"); - register int i; - - fprintf(Outfile, "%d\n", n); - - for (i = 0; i < n; ++i) - fprintf(Outfile, "%04X\n", g[i]); - - fclose(Outfile); -} - -/*********************************************************************** - TransferGraph() - edges from makeg graph g are added to theGraph. - ***********************************************************************/ - -int TransferGraph(graphP theGraph, graph *g, int n) -{ - int i, j, ErrorCode; - unsigned long PO2; - - gp_ReinitializeGraph(theGraph); - - for (i = 0, ErrorCode = OK; i < n-1 && ErrorCode==OK; i++) - { - gp_SetVertexIndex(theGraph, i+gp_GetFirstVertex(theGraph), i+gp_GetFirstVertex(theGraph)); - - PO2 = 1 << (MAXN - 1); - PO2 >>= i+1; - - for (j = i+1; j < n; j++) - { - if (g[i] & PO2) - { - ErrorCode = gp_AddEdge(theGraph, i+gp_GetFirstVertex(theGraph), 0, - j+gp_GetFirstVertex(theGraph), 0); - if (ErrorCode != OK) - { - // If we only stopped because the graph contains too - // many edges, then we determine whether or not this is - // a real error as described below - if (ErrorCode == NONEMBEDDABLE) - { - // In the default case of this implementation, the graph's - // arc capacity is set to accommodate a complete graph, - // so if there was a failure to add an edge, then some - // corruption has occurred and we report an error. - if (gp_GetArcCapacity(theGraph)/2 == (n*(n-1)/2)) - ErrorCode = NOTOK; - - // Many of the algorithms require only a small sampling - // of edges to be successful. For example, planar embedding - // and Kuratowski subgraph isolation required only 3n-5 - // edges. K3,3 search requires only 3n edges from the input - // graph. If a user modifies the test code to exploit this - // lower limit, then we permit the failure to add an edge since - // the failure to add an edge is expected. - else - ErrorCode = OK; - } - break; - } - } - - PO2 >>= 1; - } - } - - return ErrorCode; -} - -/*********************************************************************** - ***********************************************************************/ - -void outprocTest(FILE *f, graph *g, int n) -{ - if (errorFound) - return; - - if (testFramework == NULL) - { - testFramework = tf_AllocateTestFramework(g_command, n, g_maxe); - if (testFramework == NULL) - { - fprintf(g_msgfile, "\nUnable to create the test framework.\n"); - errorFound++; - } - } - - if (errorFound) - return; - - // Copy from the nauty graph to the test graph(s) - if (TransferGraph(testFramework->algResults[0].origGraph, g, n) != OK) - { - fprintf(g_msgfile, "\nFailed to initialize with generated graph in errorMatrix.txt\n"); - WriteMatrixGraph("errorMatrix.txt", g, n); - errorFound++; - } - - if (errorFound) - return; - - // gp_Write(testFramework->algResults[0].origGraph, "origGraph.txt", WRITE_ADJLIST); - - if (g_command == 'a') - { - int i, len; - - for (i=1, len=strlen(commands); i < len; i++) - { - gp_ReinitializeGraph(testFramework->algResults[i].origGraph); - if (gp_CopyAdjacencyLists(testFramework->algResults[i].origGraph, - testFramework->algResults[0].origGraph) != OK) - { - fprintf(g_msgfile, "\nFailed to copy adjacency lists\n"); - errorFound++; - return; - } - } - } - - // Run the test(s) - if (g_command == 'a') - { - int i, len; - - for (i=0, len=strlen(commands); i < len; i++) - if (runTest(f, commands[i]) != OK) - { - fprintf(g_msgfile, "See error.txt and errorMatrix.txt\n"); - gp_Write(testFramework->algResults[0].origGraph, "error.txt", WRITE_ADJLIST); - WriteMatrixGraph("errorMatrix.txt", g, n); - break; - } - } - else - { - if (runTest(f, g_command) != OK) - { - fprintf(g_msgfile, "See error.txt and errorMatrix.txt\n"); - gp_Write(testFramework->algResults[0].origGraph, "error.txt", WRITE_ADJLIST); - WriteMatrixGraph("errorMatrix.txt", g, n); - } - } - - if (quietMode == 'n') - { -#ifndef DEBUG - // In release mode, print numbers less often for faster results - if (testFramework->algResults[0].result.numGraphs % 379 == 0) -#else - // In DEBUG mode, unittestMode should still print numbers less often - if (!unittestMode || testFramework->algResults[0].result.numGraphs % 379 == 0) -#endif - { - // msgfile is used because it is mapped to stderr, wherease f is mapped to stdout - // In cases where output is redirected to a file, we don't want this count - // going to the file in case the caller forgets to set quiet mode - fprintf(g_msgfile, "\r%lu ", testFramework->algResults[0].result.numGraphs); - fflush(g_msgfile); - } - } -} - -/*********************************************************************** - ***********************************************************************/ - -int runTest(FILE *outfile, char command) -{ - int Result = OK; - testResultP testResult = tf_GetTestResult(testFramework, command); - graphP theGraph = testResult->theGraph; - graphP origGraph = testResult->origGraph; - - // Increment the main graph counter - if (++testResult->result.numGraphs == 0) - { - fprintf(g_msgfile, "\nExceeded maximum number of graphs supported by this application\n"); - fprintf(g_msgfile, "Use mine, maxe, mod and res to test in parts\n"); - errorFound = 1; - return NOTOK; - } - - // Now copy from the origGraph into theGraph on which the work will be done - if ((Result = gp_CopyGraph(theGraph, origGraph)) != OK) - { - fprintf(g_msgfile, "\nFailed to copy graph #%lu\n", testResult->result.numGraphs); - errorFound++; - return NOTOK; - } - - // Run the command on theGraph and check the integrity of the result - if (command == 'c') - { - if ((Result = gp_ColorVertices(theGraph)) != OK) - Result = NOTOK; - else - { - if (gp_ColorVerticesIntegrityCheck(theGraph, origGraph) != OK) - { - fprintf(g_msgfile, "\nIntegrity check failed on graph #%lu.\n", testResult->result.numGraphs); - Result = NOTOK; - } - if (Result == OK) - { - if (gp_GetNumColorsUsed(theGraph) >= 6) - Result = NONEMBEDDABLE; - } - } - } - else if (strchr(commands, command)) - { - int embedFlags = EMBEDFLAGS_PLANAR; - - switch (command) - { - case 'p' : embedFlags = EMBEDFLAGS_PLANAR; break; - case 'd' : embedFlags = EMBEDFLAGS_DRAWPLANAR; break; - case 'o' : embedFlags = EMBEDFLAGS_OUTERPLANAR; break; - case '2' : embedFlags = EMBEDFLAGS_SEARCHFORK23; break; - case '3' : embedFlags = EMBEDFLAGS_SEARCHFORK33; break; - case '4' : embedFlags = EMBEDFLAGS_SEARCHFORK4; break; - } - - Result = gp_Embed(theGraph, embedFlags); - - if (Result == OK || Result == NONEMBEDDABLE) - { - gp_SortVertices(theGraph); - - if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != Result) - { - Result = NOTOK; - fprintf(g_msgfile, "\nIntegrity check failed on graph #%lu.\n", testResult->result.numGraphs); - } - } - } - else Result = NOTOK; - - // Increment the counters (note that origGraph is used to get - // the number of edges M since theGraph may be a subgraph) - testResult->edgeResults[origGraph->M].numGraphs++; - if (Result == OK) - { - testResult->result.numOKs++; - testResult->edgeResults[origGraph->M].numOKs++; - } - else if (Result == NONEMBEDDABLE) - ; - else - { - errorFound++; - fprintf(g_msgfile, "\nFailed to runTest() on graph #%lu.\n", testResult->result.numGraphs); - // If the command-line shunts the output to a file, then we ensure that the file also - // contains the error message (if stdout is not redirected, then the error prints - // twice, but there's no real way around that). - fprintf(outfile, "\nFailed to runTest() on graph #%lu.\n", testResult->result.numGraphs); - } - - return Result == OK || Result == NONEMBEDDABLE ? OK : NOTOK; -} - -/*********************************************************************** - ***********************************************************************/ - -int getCommandIndex(char command) -{ - switch (command) - { - case 'p' : return 0; - case 'd' : return 1; - case 'o' : return 2; - case '2' : return 3; - case '3' : return 4; - case '4' : return 5; - case 'c' : return 6; - } - return -1; -} - -/*********************************************************************** - ***********************************************************************/ - -void getMessages(char command, char **pMsgAlg, char **pMsgOK, char **pMsgNoEmbed) -{ - switch (command) - { - case 'p' : *pMsgAlg="Planarity"; *pMsgOK="Planar"; *pMsgNoEmbed="Not Planar"; break; - case 'd' : *pMsgAlg="Planar Drawing"; *pMsgOK="Planar"; *pMsgNoEmbed="Not Planar"; break; - case 'o' : *pMsgAlg="Outerplanarity"; *pMsgOK="Embedded"; *pMsgNoEmbed="Obstructed"; break; - case '2' : *pMsgAlg="K2,3 Search"; *pMsgOK="no K2,3"; *pMsgNoEmbed="with K2,3"; break; - case '3' : *pMsgAlg="K3,3 Search"; *pMsgOK="no K3,3"; *pMsgNoEmbed="with K3,3"; break; - case '4' : *pMsgAlg="K4 Search"; *pMsgOK="no K4"; *pMsgNoEmbed="with K4"; break; - case 'c' : *pMsgAlg="Vertex Coloring"; *pMsgOK="<=5 colors"; *pMsgNoEmbed=">5 colors"; break; - default : *pMsgAlg = *pMsgOK = *pMsgNoEmbed = NULL; break; - } -} - -/*********************************************************************** - ***********************************************************************/ - -void printStats(FILE *outfile, testResultP testResult) -{ - char *msgAlg, *msgOK, *msgNoEmbed; - int j; - unsigned long numGraphs, numOKs, numNoEmbeds; - char command = testResult == NULL ? g_command : testResult->command; - int arcCapacity = testFramework == NULL ? 2*g_maxe : testFramework->algResults[0].origGraph->arcCapacity; - - getMessages(command, &msgAlg, &msgOK, &msgNoEmbed); - - fprintf(outfile, "\nBegin Stats for Algorithm %s\n", msgAlg); - fprintf(outfile, "Status=%s\n", errorFound?"ERROR":"SUCCESS"); - - fprintf(outfile, "maxn=%d, mine=%d, maxe=%d, arcCapacity=%d\n", - g_maxn, g_mine, g_maxe, arcCapacity); - if (g_mod > 1) - fprintf(outfile, "mod=%d, res=%d\n", g_mod, g_res); - - fprintf(outfile, "# Edges %10s %10s %10s\n", "# Graphs", msgOK, msgNoEmbed); - fprintf(outfile, "------- ---------- ---------- ----------\n"); - for (j = g_mine; j <= g_maxe; j++) - { - if (testResult == NULL) - numGraphs = numOKs = numNoEmbeds = 0; - else - { - numGraphs = testResult->edgeResults[j].numGraphs; - numOKs = testResult->edgeResults[j].numOKs; - numNoEmbeds = numGraphs - numOKs; - } - fprintf(outfile, "%7d %10lu %10lu %10lu\n", j, numGraphs, numOKs, numNoEmbeds); - } - - if (testResult == NULL) - numGraphs = numOKs = numNoEmbeds = 0; - else - { - numGraphs = testResult->result.numGraphs; - numOKs = testResult->result.numOKs; - numNoEmbeds = numGraphs - numOKs; - } - - fprintf(outfile, "TOTALS %10lu %10lu %10lu\n", numGraphs, numOKs, numNoEmbeds); - - fprintf(outfile, "End Stats for Algorithm %s\n", msgAlg); -} - -/*********************************************************************** - Test_PrintStats() - called by makeg to print the final stats. - ***********************************************************************/ - -void Test_PrintStats(FILE *outfile) -{ - // If no graphs were generated, then we need to generate an initialized testFramework - if (testFramework == NULL) - { - testFramework = tf_AllocateTestFramework(g_command, g_maxn, g_maxe); - if (testFramework == NULL) - { - fprintf(g_msgfile, "\nUnable to create the test framework.\n"); - errorFound++; - return; - } - } - - // Need to print the final count of graphs, since the counter is usually modulated due to cost - if (quietMode == 'n' && testFramework != NULL) - fprintf(g_msgfile, "\r%lu \n", testFramework->algResults[0].result.numGraphs); - - // The unit test mode has special case handling to test whether the stat are correct - if (unittestMode) - { - unsigned long results[NUMCOMMANDSTOTEST] = { 194815, 194815, 269377, 268948, 191091, 265312, 2178 }; - int i; - testResultP testResult; - - if (g_command == 'a') - { - for (i=0; i < NUMCOMMANDSTOTEST; i++) - { - testResult = testFramework->algResults + i; - if (results[i] != testResult->result.numGraphs - testResult->result.numOKs) - errorFound = 1; - } - } - else - { - i = getCommandIndex(g_command); - - if (i < 0 || i >= NUMCOMMANDSTOTEST) - errorFound = 1; - else - { - testResult = testFramework->algResults + 0; - if (results[i] != testResult->result.numGraphs - testResult->result.numOKs) - errorFound = 1; - } - } - } - - // Iterated stats printing in "all commands" mode - else if (g_command == 'a') - { - int i; - for (i=0; i < testFramework->algResultsSize; i++) - printStats(outfile, &testFramework->algResults[i]); - } - - // The basis case of printing stats for a single command - else - { - printStats(outfile, &testFramework->algResults[0]); - } - - // Done with the framework - tf_FreeTestFramework(&testFramework); - - // Done with the error flag, except unittestMode needs the result and must clear it - if (!unittestMode) - errorFound = 0; -} diff --git a/planarity_src/c/nauty/outproc.h b/planarity_src/c/nauty/outproc.h deleted file mode 100644 index 6a0d365..0000000 --- a/planarity_src/c/nauty/outproc.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef OUTPROC_H -#define OUTPROC_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2010, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -* 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. - -* 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. - -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 Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -void outprocTest(FILE *f, graph *g, int n); -void Test_PrintStats(FILE *); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/planarity_src/c/nauty/testFramework.c b/planarity_src/c/nauty/testFramework.c deleted file mode 100644 index 53fd4ba..0000000 --- a/planarity_src/c/nauty/testFramework.c +++ /dev/null @@ -1,240 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2010, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -* 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. - -* 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. - -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 Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include - -#include "testFramework.h" - -char *commands = "pdo234c"; - -#include "../graphK23Search.h" -#include "../graphK33Search.h" -#include "../graphK4Search.h" -#include "../graphDrawPlanar.h" -#include "../graphColorVertices.h" - -/* Forward Declarations of Private functions */ -graphP createGraph(char command, int n, int maxe); -int attachAlgorithmExtension(char command, graphP aGraph); -void initBaseTestResult(baseTestResultStruct *pBaseResult); -int initTestResult(testResultP testResult, char command, int n, int maxe); -void releaseTestResult(testResultP testResult); - -/*********************************************************************** - tf_AllocateTestFramework() - ***********************************************************************/ - -testResultFrameworkP tf_AllocateTestFramework(char command, int n, int maxe) -{ - testResultFrameworkP framework = (testResultFrameworkP) malloc(sizeof(testResultFrameworkStruct)); - - if (framework != NULL) - { - int i; - - framework->algResultsSize = command == 'a' ? NUMCOMMANDSTOTEST : 1; - framework->algResults = (testResultP) malloc(framework->algResultsSize * sizeof(testResultStruct)); - for (i=0; i < framework->algResultsSize; i++) - if (initTestResult(framework->algResults+i, - command=='a'?commands[i]:command, n, maxe) != OK) - { - framework->algResultsSize = i; - tf_FreeTestFramework(&framework); - return NULL; - } - } - - return framework; -} - -/*********************************************************************** - tf_FreeTestFramework() - ***********************************************************************/ - -void tf_FreeTestFramework(testResultFrameworkP *pTestFramework) -{ - if (pTestFramework != NULL && *pTestFramework != NULL) - { - testResultFrameworkP framework = *pTestFramework; - - if (framework->algResults != NULL) - { - int i; - for (i=0; i < framework->algResultsSize; i++) - releaseTestResult(framework->algResults+i); - - free((void *) framework->algResults); - framework->algResults = NULL; - framework->algResultsSize = 0; - } - - free(*pTestFramework); - *pTestFramework = framework = NULL; - } -} - -/*********************************************************************** - tf_GetTestResult() - ***********************************************************************/ - -testResultP tf_GetTestResult(testResultFrameworkP framework, char command) -{ - testResultP testResult = NULL; - - if (framework != NULL && framework->algResults != NULL) - { - int i; - for (i=0; i < framework->algResultsSize; i++) - if (framework->algResults[i].command == command) - testResult = framework->algResults+i; - } - - return testResult; -} - -/*********************************************************************** - initTestResult() - ***********************************************************************/ - -int initTestResult(testResultP testResult, char command, int n, int maxe) -{ - if (testResult != NULL) - { - testResult->command = command; - initBaseTestResult(&testResult->result); - testResult->edgeResults = NULL; - testResult->edgeResultsSize = maxe; - testResult->theGraph = NULL; - testResult->origGraph = NULL; - - testResult->edgeResults = (baseTestResultStruct *) malloc((maxe+1) * sizeof(baseTestResultStruct)); - if (testResult->edgeResults == NULL) - { - releaseTestResult(testResult); - return NOTOK; - } - else - { - int j; - for (j=0; j <= maxe; j++) - initBaseTestResult(&testResult->edgeResults[j]); - } - - if ((testResult->theGraph = createGraph(command, n, maxe)) == NULL || - (testResult->origGraph = createGraph(command, n, maxe)) == NULL) - { - releaseTestResult(testResult); - return NOTOK; - } - } - - return OK; -} - -/*********************************************************************** - releaseTestResult() - Releases all memory resources used by the testResult, but does not - free the testResult since it points into an array. - ***********************************************************************/ - -void releaseTestResult(testResultP testResult) -{ - if (testResult->edgeResults != NULL) - { - free(testResult->edgeResults); - testResult->edgeResults = NULL; - } - gp_Free(&(testResult->theGraph)); - gp_Free(&(testResult->origGraph)); -} - -/*********************************************************************** - initBaseTestResult() - ***********************************************************************/ - -void initBaseTestResult(baseTestResultStruct *pBaseResult) -{ - memset(pBaseResult, 0, sizeof(baseTestResultStruct)); -} - -/*********************************************************************** - createGraph() - ***********************************************************************/ - -graphP createGraph(char command, int n, int maxe) -{ - graphP theGraph; - int numArcs = 2*(maxe > 0 ? maxe : 1); - - if ((theGraph = gp_New()) != NULL) - { - if (gp_EnsureArcCapacity(theGraph, numArcs) != OK || - gp_InitGraph(theGraph, n) != OK || - attachAlgorithmExtension(command, theGraph) != OK) - gp_Free(&theGraph); - } - - return theGraph; -} - -/*********************************************************************** - attachAlgorithmExtension() - ***********************************************************************/ - -int attachAlgorithmExtension(char command, graphP aGraph) -{ - switch (command) - { - case 'p' : break; - case 'd' : gp_AttachDrawPlanar(aGraph); break; - case 'o' : break; - case '2' : gp_AttachK23Search(aGraph); break; - case '3' : gp_AttachK33Search(aGraph); break; - case '4' : gp_AttachK4Search(aGraph); break; - case 'c' : gp_AttachColorVertices(aGraph); break; - default : return NOTOK; - } - - return OK; -} diff --git a/planarity_src/c/nauty/testFramework.h b/planarity_src/c/nauty/testFramework.h deleted file mode 100644 index 9d0d11c..0000000 --- a/planarity_src/c/nauty/testFramework.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef TESTFRAMEWORK_H -#define TESTFRAMEWORK_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2010, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -* 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. - -* 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. - -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 Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "../graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define NUMCOMMANDSTOTEST 7 -extern char *commands; - -// numGraphs: the number of graphs that met the test criteria -// (e.g. were generated and had a specific number of edges) -// numOKs: the number of graphs on which the test produced OK -// (as opposed to NONEMBEDDABLE (or NOTOK)) -typedef struct -{ - unsigned long numGraphs; - unsigned long numOKs; -} baseTestResultStruct; - -// command: Indicates the algorithm being tested -// result: Cumulative result of tests on graphs of all sizes -// (i.e. numbers of edges; the number of vertices is fixed -// within a given test) -// edgeResults*: Array to accumulateresult of tests on graphs of a -// each fixed size (i.e. fixed number of edges) -// theGraph: a graph data structure on which each test is performed -// The graph is preconfigured with a number of vertices -// and a specific algorithm extension. -// origGraph: a copy of the graph being tested before the algorithm -// is run on it. This is needed for integrity checking of -// an algorithm's result produced in theGraph -// The origGraph from algResult[0] in th test framework -// receives the edges from the adjacency matrix graph -// generated by Nauty. Then, gp_CopyAdjacencyLists() is -// used to copy the edges of this graph into the origGraph -// of each other algorithm testResult (if any). -typedef struct -{ - char command; - baseTestResultStruct result; - baseTestResultStruct *edgeResults; - int edgeResultsSize; - graphP theGraph, origGraph; -} testResultStruct; - -typedef testResultStruct * testResultP; - -// algResults: An array of testResults for each algorithm being tested -// algResultsSize: the number of algorithms being tested -typedef struct -{ - testResultStruct *algResults; - int algResultsSize; -} testResultFrameworkStruct; - -typedef testResultFrameworkStruct * testResultFrameworkP; - -// Returns a pointer to the testResult for the given command character -// or NULL if there is no test result for the algorithm -testResultP tf_GetTestResult(testResultFrameworkP framework, char command); - -// Allocate the test framework for the given command. The number of vertices -// and maximum number of edges in the graphs to be tested are also given. -testResultFrameworkP tf_AllocateTestFramework(char command, int n, int maxe); - -// Free the test framework. -void tf_FreeTestFramework(testResultFrameworkP *pTestFramework); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/planarity_src/c/planarity.c b/planarity_src/c/planarity.c deleted file mode 100644 index 3845b39..0000000 --- a/planarity_src/c/planarity.c +++ /dev/null @@ -1,326 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "planarity.h" - -void ProjectTitle() -{ - Message("\n==================================================" - "\nPlanarity version 3.0.0.1" - "\nCopyright (c) 2012 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" - " -c = Color the vertices of the graph\n" - " -a = All of the above\n" - "\n"; - - ProjectTitle(); - - if (param == NULL) - { - Message( - "'planarity': if no command-line, then menu-driven\n" - "'planarity (-h|-help)': this message\n" - "'planarity (-h|-help) -gen': more help with graph generator command line\n" - "'planarity (-h|-help) -menu': more help with menu-based command line\n" - "'planarity (-i|-info): copyright and license information\n" - "'planarity -test [-q] [C]': runs tests (optional quiet mode, single test)\n" - "'planarity -gen [-q] C [...] n [...]': run command C on n-vertex graphs\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( - "Planarity-Related Graph Algorithms Project\n" - "Copyright (c) 1997-2012, John M. Boyer\n" - "All rights reserved. Includes a reference implementation of the following:\n" - "\n" - "* John M. Boyer. \"Subgraph Homeomorphism via the Edge Addition Planarity \n" - " Algorithm\". Journal of Graph Algorithms and Applications, Vol. 16, \n" - " no. 2, pp. 381-410, 2012. http://www.jgaa.info/16/268.html\n" - "\n" - "* John M. Boyer. \"A New Method for Efficiently Generating Planar Graph\n" - " Visibility Representations\". In P. Eades and P. Healy, editors,\n" - " Proceedings of the 13th International Conference on Graph Drawing 2005,\n" - " Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.\n" - "\n" - "* John M. Boyer and Wendy J. Myrvold. \"On the Cutting Edge: Simplified O(n)\n" - " Planarity by Edge Addition\". Journal of Graph Algorithms and Applications,\n" - " Vol. 8, No. 3, pp. 241-273, 2004.\n" - " http://www.jgaa.info/08/91.html\n" - "\n" - "* John M. Boyer. \"Simplified O(n) Algorithms for Planar Graph Embedding,\n" - " Kuratowski Subgraph Isolation, and Related Problems\". Ph.D. Dissertation,\n" - " University of Victoria, 2001.\n" - "\n" - "Redistribution and use in source and binary forms, with or without \n" - "modification, are permitted provided that the following conditions are met:\n" - "\n" - "* Redistributions of source code must retain the above copyright notice, this\n" - " list of conditions and the following disclaimer.\n" - "\n" - "* Redistributions in binary form must reproduce the above copyright notice, \n" - " this list of conditions and the following disclaimer in the documentation \n" - " and/or other materials provided with the distribution.\n" - "\n" - "* Neither the name of the Planarity-Related Graph Algorithms Project nor the \n" - " names of its contributors may be used to endorse or promote products derived \n" - " from this software without specific prior written permission.\n" - "\n" - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" - "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" - "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n" - "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n" - "ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n" - "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" - "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n" - "ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" - "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n" - "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - ); - } - - else if (strcmp(param, "-gen") == 0) - { - Message( - "'planarity -gen [-q] C {ncl}': run command C on graphs generated by makeg,\n" - " part of McKay's Nauty program. See Nauty command line {ncl} below.\n" - " Results are integrity-checked; stats per number of edges are given.\n" - ); - - Message(commandStr); - - Message("{ncl}= [-c -t -b] [-d] n [mine [maxe [mod res]]]\n\n"); - - Message( - "n = the number of vertices (1..16)\n" - "mine = the minimum number of edges (no bounds if missing)\n" - "maxe = the maximum number of edges (same as mine if missing)\n" - "mod, res = a way to restrict the output to a subset.\n" - " All the graphs in G(n,mine..maxe) are divided into\n" - " disjoint classes C(mod,0),C(mod,1),...,C(mod,mod-1),\n" - " of very approximately equal size.\n" - " Only the class C(mod,res) is generated.\n" - " The usual relationships between modulo classes are\n" - " obeyed; for example C(4,3) = C(8,3) union C(8,7).\n" - "-c : only generate connected graphs\n" - "-t : only generate triangle-free graphs\n" - "-b : only generate bipartite graphs\n" - "-d : specify an upper bound for the maximum degree.\n" - " The value must be adjacent to the 'd', e.g. -d6.\n" - ); - } - - else if (strcmp(param, "-menu") == 0) - { - Message( - "'planarity -r [-q] C K N': Random graphs\n" - "'planarity -s [-q] C I O [O2]': Specific graph\n" - "'planarity -rm [-q] N O [O2]': Maximal planar random graph\n" - "'planarity -rn [-q] N O [O2]': Nonplanar random graph (maximal planar + edge)\n" - "'planarity I O [-n O2]': Legacy command-line (default -s -p)\n" - "\n" - ); - - Message("-q is for quiet mode (no messages to stdout and stderr)\n\n"); - - Message(commandStr); - - Message( - "K = # of graphs to randomly generate\n" - "N = # of vertices in each randomly generated graph\n" - "I = Input file (for work on a specific graph)\n" - "O = Primary output file\n" - " For example, if C=-p then O receives the planar embedding\n" - " If C=-3, then O receives a subgraph containing a K_{3,3}\n" - "O2= Secondary output file\n" - " For -s, if C=-p or -o, then O2 receives the embedding obstruction\n" - " For -s, if C=-d, then O2 receives a drawing of the planar graph\n" - " For -m and -n, O2 contains the original randomly generated graph\n" - "\n" - ); - - Message( - "planarity process results: 0=OK, -1=NOTOK, 1=NONEMBEDDABLE\n" - " 1 result only produced by specific graph mode (-s)\n" - " with command -2,-3,-4: found K_{2,3}, K_{3,3} or K_4\n" - " with command -p,-d: found planarity obstruction\n" - " with command -o: found outerplanarity obstruction\n" - ); - } - - FlushConsole(stdout); - return 0; -} - -/**************************************************************************** - MENU-DRIVEN PROGRAM - ****************************************************************************/ - -int menu() -{ -char Choice; - - do { - ProjectTitle(); - - Message("\n" - "P. Planar embedding and Kuratowski subgraph isolation\n" - "D. Planar graph drawing by visibility representation\n" - "O. Outerplanar embedding and obstruction isolation\n" - "2. Search for subgraph homeomorphic to K_{2,3}\n" - "3. Search for subgraph homeomorphic to K_{3,3}\n" - "4. Search for subgraph homeomorphic to K_4\n" - "C. Color the vertices of the graph\n" - "H. Help message for command line version\n" - "R. Reconfigure options\n" - "X. Exit\n" - "\n" - ); - - Prompt("Enter Choice: "); - fflush(stdin); - scanf(" %c", &Choice); - Choice = tolower(Choice); - - if (Choice == 'h') - helpMessage(NULL); - - else if (Choice == 'r') - Reconfigure(); - - else if (Choice != 'x') - { - char *secondOutfile = NULL; - if (Choice == 'p' || Choice == 'o' || Choice == 'd') - secondOutfile =""; - - switch (tolower(Mode)) - { - case 's' : SpecificGraph(Choice, NULL, NULL, secondOutfile); break; - case 'r' : RandomGraphs(Choice, 0, 0); break; - case 'm' : RandomGraph(Choice, 0, 0, NULL, NULL); break; - case 'n' : RandomGraph(Choice, 1, 0, NULL, NULL); break; - } - } - - if (Choice != 'r' && Choice != 'x') - { - Prompt("\nPress a key then hit ENTER to continue..."); - fflush(stdin); - scanf(" %*c"); - fflush(stdin); - Message("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - FlushConsole(stdout); - } - - } while (Choice != 'x'); - - // Certain debuggers don't terminate correctly with pending output content - FlushConsole(stdout); - FlushConsole(stderr); - - return 0; -} diff --git a/planarity_src/c/planarity.h b/planarity_src/c/planarity.h deleted file mode 100644 index 370b690..0000000 --- a/planarity_src/c/planarity.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef PLANARITY_H -#define PLANARITY_H - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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" -#include "graphColorVertices.h" - -void ProjectTitle(); -int helpMessage(char *param); - -/* Functions that call the Graph Library */ -int SpecificGraph(char command, char *infileName, char *outfileName, char *outfile2Name); -int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name); -int RandomGraphs(char command, int, int); - -int makeg_main(char command, int argc, char *argv[]); - -/* Command line, Menu, and Configuration */ -int commandLine(int argc, char *argv[]); -int legacyCommandLine(int argc, char *argv[]); -int menu(); - -char Mode, - OrigOut, - EmbeddableOut, - ObstructedOut, - AdjListsForEmbeddingsOut, - quietMode; - -void Reconfigure(); - -/* Low-level Utilities */ -#define MAXLINE 1024 -char Line[MAXLINE]; - -void Message(char *message); -void ErrorMessage(char *message); -void FlushConsole(FILE *f); -void Prompt(char *message); - -void SaveAsciiGraph(graphP theGraph, char *filename); - -int FilesEqual(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 diff --git a/planarity_src/c/planarityCommandLine.c b/planarity_src/c/planarityCommandLine.c deleted file mode 100644 index 260bc9c..0000000 --- a/planarity_src/c/planarityCommandLine.c +++ /dev/null @@ -1,605 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "planarity.h" - -#include - -int callNauty(int argc, char *argv[]); -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], "-gen") == 0 || strcmp(argv[1], "-gens") == 0) - Result = callNauty(argc, argv); - - else if (strcmp(argv[1], "-test") == 0) - Result = runQuickRegressionTests(argc, argv); - - else if (strcmp(argv[1], "-r") == 0) - Result = callRandomGraphs(argc, argv); - - else if (strcmp(argv[1], "-s") == 0) - Result = callSpecificGraph(argc, argv); - - else if (strcmp(argv[1], "-rm") == 0) - Result = callRandomMaxPlanarGraph(argc, argv); - - else if (strcmp(argv[1], "-rn") == 0) - Result = callRandomNonplanarGraph(argc, argv); - - else - { - ErrorMessage("Unsupported command line. Here is the help for this program.\n"); - helpMessage(NULL); - Result = NOTOK; - } - - return Result == OK ? 0 : (Result == NONEMBEDDABLE ? 1 : -1); -} - -/**************************************************************************** - Legacy Command Line Processor from version 1.x - ****************************************************************************/ - -int legacyCommandLine(int argc, char *argv[]) -{ -graphP theGraph = gp_New(); -int Result; - - Result = gp_Read(theGraph, argv[1]); - if (Result != OK) - { - if (Result != NONEMBEDDABLE) - { - if (strlen(argv[1]) > MAXLINE - 100) - sprintf(Line, "Failed to read graph\n"); - else - sprintf(Line, "Failed to read graph %s\n", argv[1]); - ErrorMessage(Line); - return -2; - } - } - - Result = gp_Embed(theGraph, EMBEDFLAGS_PLANAR); - - if (Result == OK) - { - gp_SortVertices(theGraph); - gp_Write(theGraph, argv[2], WRITE_ADJLIST); - } - - else if (Result == NONEMBEDDABLE) - { - if (argc >= 5 && strcmp(argv[3], "-n")==0) - { - gp_SortVertices(theGraph); - gp_Write(theGraph, argv[4], WRITE_ADJLIST); - } - } - else - Result = NOTOK; - - gp_Free(&theGraph); - - // In the legacy 1.x versions, OK/NONEMBEDDABLE was 0 and NOTOK was -2 - return Result==OK || Result==NONEMBEDDABLE ? 0 : -2; -} - -/**************************************************************************** - WriteTestFiles() - A one-time-run piece of code that generates batch files which perform the - incremental tests of all 12 vertex graphs. - - The format of the command line is: - planarity -gen -q -a n j j 12 i > TestResult_n_j_j_12_i.txt - - The n is for the number of vertices. - The j j is the number of edges, between 0 and n(n-1)/2. - The mod divides the problem into partitions that can be run on separate threads. - The i is a partition number between 0 and mod-1. Each test batch file receives - a number i, and within it are all the command lines for that partition to test - each number of edges. - The numbers of edges are tested separately because the implementation makes - a smaller data structure for graphs that are of a guaranteed maximum size. - ****************************************************************************/ - -void WriteTestFiles(int n, int mod) -{ - char filename[64]; - FILE *outfile; - int k, e; - int maxe=n*(n-1)/2; - - for (k = 0; k < mod; k++) - { - sprintf(filename, "test_n%02d\\test_n%02d_mod%02d.bat", n, n, k); - outfile = fopen(filename, "wt"); - if (outfile == NULL) - { - printf("Error creating test file %s\nRemember to create directory Test_n%02d\n", filename, n); - return; - } - - for (e = 0; e <= maxe; e++) - { - sprintf(filename, "results\\result_%02d_%02d_%02d_%02d_%02d.txt", n, e, e, mod, k); - fprintf(outfile, "..\\planarity -gen -a %d %d %d %d %d > %s\n", n, e, e, mod, k, filename); - } - fclose(outfile); - } - - printf("Created test files. Remember to create 'results' in test subdirectory\n"); -} - -/**************************************************************************** - Call Nauty's MAKEG graph generator. - ****************************************************************************/ - -// 'planarity -gen [-q] C {ncl}': exhaustive tests with graphs generated by makeg, -// from McKay's Nauty package -// makeg [-c -t -b] [-d] n [mine [maxe [mod res]]] - -//// These externs can be obtained for debugging -////extern unsigned long numGraphs; -////extern unsigned long numErrors; -////extern unsigned long numOKs; - -int callNauty(int argc, char *argv[]) -{ - char command; - int numArgs, argsOffset, i; - char *args[12]; - int result; - platform_time start, end; - -// WriteTestFiles(11, 12); -// WriteTestFiles(12, 12); - - if (argc < 4 || argc > 12) - return -1; - - // Determine the offset of the arguments after command C - argsOffset = (argv[2][0] == '-' && argv[2][1] == 'q') ? 3 : 2; - - // Obtain the command C - command = argv[argsOffset][1]; - - // Same number of args, except exclude -gen, the optional -q, and the command C - numArgs = argc-argsOffset; - - // Change 0th arg from planarity to makeg - args[0] = "makeg"; - - // Copy the rest of the args from argv, except -gen and command C - for (i=1; i < numArgs; i++) - args[i] = argv[i+argsOffset]; - - // Generate order N graphs of all sizes requested on command line - platform_GetTime(start); - result = makeg_main(command, numArgs, args) == 0 ? 0 : -1; - platform_GetTime(end); - - printf("\nTotal time = %.3lf seconds\n", platform_GetDuration(start,end)); - return result; -} - -/**************************************************************************** - Quick regression test - ****************************************************************************/ - -int runNautyTests(int argc, char *argv[]); -int runSpecificGraphTests(); -int runSpecificGraphTest(char *command, char *infileName); - -int runQuickRegressionTests(int argc, char *argv[]) -{ - if (runSpecificGraphTests() < 0) - return -1; - - return runNautyTests(argc, argv); -} - -int runSpecificGraphTests() -{ - char origDir[2049]; - int retVal = 0; - - if (!getcwd(origDir, 2048)) - return -1; - - if (chdir("samples") != 0) - { - if (chdir("..") != 0 || chdir("samples") != 0) - { - // Warn but give success result - printf("WARNING: Unable to change to samples directory to run tests on samples.\n"); - return 0; - } - } - -#if NIL == 0 - if (runSpecificGraphTest("-p", "maxPlanar5.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "maxPlanar5.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "drawExample.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-p", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-o", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-2", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-3", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-4", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-c", "maxPlanar5.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-c", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-c", "drawExample.txt") < 0) - retVal = -1; -#endif - - if (runSpecificGraphTest("-p", "maxPlanar5.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "maxPlanar5.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "drawExample.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-p", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-o", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-2", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-3", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-4", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-c", "maxPlanar5.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-c", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-c", "drawExample.0-based.txt") < 0) - retVal = -1; - - if (retVal == 0) - printf("Tests of all specific graphs succeeded\n"); - - chdir(origDir); - FlushConsole(stdout); - return retVal; -} - -int runSpecificGraphTest(char *command, char *infileName) -{ - char *commandLine[] = { - "planarity", "-s", "C", "infile", "outfile", "outfile2" - }; - char *outfileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]); - char *outfile2Name = ""; - char *testfileName = strdup(outfileName); - int Result = 0; - - if (testfileName == NULL) - return -1; - - outfileName = strdup(strcat(outfileName, ".test.txt")); - if (outfileName == NULL) - { - free(testfileName); - return -1; - } - - // 'planarity -s [-q] C I O [O2]': Specific graph - commandLine[2] = command; - commandLine[3] = infileName; - commandLine[4] = outfileName; - commandLine[5] = outfile2Name; - - Result = callSpecificGraph(6, commandLine); - if (Result == OK || Result == NONEMBEDDABLE) - Result = 0; - else - { - ErrorMessage("Test failed (graph processor returned failure result).\n"); - Result = -1; - } - - if (Result == 0) - { - if (FilesEqual(testfileName, outfileName) == TRUE) - { - Message("Test succeeded (result equal to exemplar).\n"); - unlink(outfileName); - } - else - { - ErrorMessage("Test failed (result not equal to exemplar).\n"); - Result = -1; - } - } - - // For graph drawing, secondary file is outfileName + ".render.txt" - - if (command[1] == 'd' && Result == 0) - { - outfile2Name = ConstructPrimaryOutputFilename(NULL, outfileName, command[1]); - free(outfileName); - outfileName = strdup(strcat(outfile2Name, ".render.txt")); - - free(testfileName); - testfileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]); - testfileName = strdup(strcat(testfileName, ".render.txt")); - - if (Result == 0) - { - if (FilesEqual(testfileName, outfileName) == TRUE) - { - Message("Test succeeded (secondary result equal to exemplar).\n"); - unlink(outfileName); - } - else - { - ErrorMessage("Test failed (secondary result not equal to exemplar).\n"); - Result = -1; - } - } - } - - Message("\n"); - - free(outfileName); - free(testfileName); - return Result; -} - -#include "nauty/testFramework.h" -extern int unittestMode; -extern int errorFound; -//extern testResultFrameworkP testFramework; - -int runNautyTests(int argc, char *argv[]) -{ - platform_time start, end; - int success = TRUE; - char *commandLine[4] = { - "planarity", "-gen", "-a", "9" - }; - - platform_GetTime(start); - - // If a single command test, then get the command, otherwise restore 'a' for all - if (argc == 4 || (argc == 3 && quietMode != 'y')) - commandLine[2] = argv[2 + (quietMode == 'y' ? 1 : 0)]; - else - commandLine[2] = "-a"; - - // Go to unit test mode, tell the user what is happening - unittestMode = 1; - printf("Testing all %s vertex graphs\n", commandLine[3]); - - // Run the test and obtain the result - if (callNauty(4, commandLine) != 0) - success = FALSE; - else - { - if (errorFound) - success = FALSE; - errorFound = 0; - } - - // Get out of unit test mode - unittestMode = 0; - - // Report the time duration and the result - platform_GetTime(end); - printf("\nFinished all processing in %.3lf seconds.\n", platform_GetDuration(start,end)); - printf("Status = %s\n", success ? "SUCCESS" : "ERROR"); - - return success ? 0 : -1; -} - -/**************************************************************************** - callRandomGraphs() - ****************************************************************************/ - -// 'planarity -r [-q] C K N': Random graphs -int callRandomGraphs(int argc, char *argv[]) -{ - char Choice = 0; - int offset = 0, NumGraphs, SizeOfGraphs; - - if (argc < 5) - return -1; - - if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q') - { - Choice = argv[3][1]; - if (argc < 6) - return -1; - offset = 1; - } - - NumGraphs = atoi(argv[3+offset]); - SizeOfGraphs = atoi(argv[4+offset]); - - return RandomGraphs(Choice, NumGraphs, SizeOfGraphs); -} - -/**************************************************************************** - callSpecificGraph() - ****************************************************************************/ - -// 'planarity -s [-q] C I O [O2]': Specific graph -int callSpecificGraph(int argc, char *argv[]) -{ - char Choice=0, *infileName=NULL, *outfileName=NULL, *outfile2Name=NULL; - int offset = 0; - - if (argc < 5) - return -1; - - if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q') - { - Choice = argv[3][1]; - if (argc < 6) - return -1; - offset = 1; - } - - infileName = argv[3+offset]; - outfileName = argv[4+offset]; - if (argc == 6+offset) - outfile2Name = argv[5+offset]; - - return SpecificGraph(Choice, infileName, outfileName, outfile2Name); -} - -/**************************************************************************** - callRandomMaxPlanarGraph() - ****************************************************************************/ - -// 'planarity -rm [-q] N O [O2]': Maximal planar random graph -int callRandomMaxPlanarGraph(int argc, char *argv[]) -{ - int offset = 0, numVertices; - char *outfileName = NULL, *outfile2Name = NULL; - - if (argc < 4) - return -1; - - if (argv[2][0] == '-' && argv[2][1] == 'q') - { - if (argc < 5) - return -1; - offset = 1; - } - - numVertices = atoi(argv[2+offset]); - outfileName = argv[3+offset]; - if (argc == 5+offset) - outfile2Name = argv[4+offset]; - - return RandomGraph('p', 0, numVertices, outfileName, outfile2Name); -} - -/**************************************************************************** - callRandomNonplanarGraph() - ****************************************************************************/ - -// 'planarity -rn [-q] N O [O2]': Non-planar random graph (maximal planar plus edge) -int callRandomNonplanarGraph(int argc, char *argv[]) -{ - int offset = 0, numVertices; - char *outfileName = NULL, *outfile2Name = NULL; - - if (argc < 4) - return -1; - - if (argv[2][0] == '-' && argv[2][1] == 'q') - { - if (argc < 5) - return -1; - offset = 1; - } - - numVertices = atoi(argv[2+offset]); - outfileName = argv[3+offset]; - if (argc == 5+offset) - outfile2Name = argv[4+offset]; - - return RandomGraph('p', 1, numVertices, outfileName, outfile2Name); -} diff --git a/planarity_src/c/planarityRandomGraphs.c b/planarity_src/c/planarityRandomGraphs.c deleted file mode 100644 index 7c81aa1..0000000 --- a/planarity_src/c/planarityRandomGraphs.c +++ /dev/null @@ -1,471 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "planarity.h" - -void GetNumberIfZero(int *pNum, char *prompt, int min, int max); -void ReinitializeGraph(graphP *pGraph, int ReuseGraphs, char command); -graphP MakeGraph(int Size, char command); - -/**************************************************************************** - RandomGraphs() - Top-level method to randomly generate graphs to test the algorithm given by - the command parameter. - The number of graphs to generate, and the number of vertices for each graph, - can be sent as the second and third params. For each that is sent as zero, - this method will prompt the user for a value. - ****************************************************************************/ - -#define NUM_MINORS 9 - -int RandomGraphs(char command, int NumGraphs, int SizeOfGraphs) -{ -char theFileName[256]; -int K, countUpdateFreq; -int Result=OK, MainStatistic=0; -int ObstructionMinorFreqs[NUM_MINORS]; -graphP theGraph=NULL, origGraph=NULL; -platform_time start, end; -int embedFlags = GetEmbedFlags(command); -int ReuseGraphs = TRUE; - - GetNumberIfZero(&NumGraphs, "Enter number of graphs to generate:", 1, 1000000000); - GetNumberIfZero(&SizeOfGraphs, "Enter size of graphs:", 1, 10000); - - theGraph = MakeGraph(SizeOfGraphs, command); - origGraph = MakeGraph(SizeOfGraphs, command); - if (theGraph == NULL || origGraph == NULL) - { - gp_Free(&theGraph); - return NOTOK; - } - - // Initialize a secondary statistics array - for (K=0; K < NUM_MINORS; K++) - ObstructionMinorFreqs[K] = 0; - - // Seed the random number generator with "now". Do it after any prompting - // to tie randomness to human process of answering the prompt. - srand(time(NULL)); - - // Select a counter update frequency that updates more frequently with larger graphs - // and which is relatively prime with 10 so that all digits of the count will change - // even though we aren't showing the count value on every iteration - countUpdateFreq = 3579 / SizeOfGraphs; - countUpdateFreq = countUpdateFreq < 1 ? 1 : countUpdateFreq; - countUpdateFreq = countUpdateFreq % 2 == 0 ? countUpdateFreq+1 : countUpdateFreq; - countUpdateFreq = countUpdateFreq % 5 == 0 ? countUpdateFreq+2 : countUpdateFreq; - - // Start the count - fprintf(stdout, "0\r"); - fflush(stdout); - - // Start the timer - platform_GetTime(start); - - // Generate and process the number of graphs requested - for (K=0; K < NumGraphs; K++) - { - if ((Result = gp_CreateRandomGraph(theGraph)) == OK) - { - if (tolower(OrigOut)=='y') - { - sprintf(theFileName, "random\\%d.txt", K%10); - gp_Write(theGraph, theFileName, WRITE_ADJLIST); - } - - gp_CopyGraph(origGraph, theGraph); - - if (strchr("pdo234", command)) - { - Result = gp_Embed(theGraph, embedFlags); - - if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != Result) - Result = NOTOK; - - if (Result == OK) - { - MainStatistic++; - - if (tolower(EmbeddableOut) == 'y') - { - sprintf(theFileName, "embedded\\%d.txt", K%10); - gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); - } - - if (tolower(AdjListsForEmbeddingsOut) == 'y') - { - sprintf(theFileName, "adjlist\\%d.txt", K%10); - gp_Write(theGraph, theFileName, WRITE_ADJLIST); - } - } - else if (Result == NONEMBEDDABLE) - { - if (embedFlags == EMBEDFLAGS_PLANAR || embedFlags == EMBEDFLAGS_OUTERPLANAR) - { - if (theGraph->IC.minorType & MINORTYPE_A) - ObstructionMinorFreqs[0] ++; - else if (theGraph->IC.minorType & MINORTYPE_B) - ObstructionMinorFreqs[1] ++; - else if (theGraph->IC.minorType & MINORTYPE_C) - ObstructionMinorFreqs[2] ++; - else if (theGraph->IC.minorType & MINORTYPE_D) - ObstructionMinorFreqs[3] ++; - else if (theGraph->IC.minorType & MINORTYPE_E) - ObstructionMinorFreqs[4] ++; - - if (theGraph->IC.minorType & MINORTYPE_E1) - ObstructionMinorFreqs[5] ++; - else if (theGraph->IC.minorType & MINORTYPE_E2) - ObstructionMinorFreqs[6] ++; - else if (theGraph->IC.minorType & MINORTYPE_E3) - ObstructionMinorFreqs[7] ++; - else if (theGraph->IC.minorType & MINORTYPE_E4) - ObstructionMinorFreqs[8] ++; - - if (tolower(ObstructedOut) == 'y') - { - sprintf(theFileName, "obstructed\\%d.txt", K%10); - gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); - } - } - } - } - else if (command == 'c') - { - if ((Result = gp_ColorVertices(theGraph)) == OK) - Result = gp_ColorVerticesIntegrityCheck(theGraph, origGraph); - if (Result == OK && gp_GetNumColorsUsed(theGraph) <= 5) - MainStatistic++; - } - - // If there is an error in processing, then write the file for debugging - if (Result != OK && Result != NONEMBEDDABLE) - { - sprintf(theFileName, "error\\%d.txt", K%10); - gp_Write(origGraph, theFileName, WRITE_ADJLIST); - } - } - - // Reinitialize or recreate graphs for next iteration - ReinitializeGraph(&theGraph, ReuseGraphs, command); - ReinitializeGraph(&origGraph, ReuseGraphs, command); - - // Show progress, but not so often that it bogs down progress - if (quietMode == 'n' && (K+1) % countUpdateFreq == 0) - { - fprintf(stdout, "%d\r", K+1); - fflush(stdout); - } - - // Terminate loop on error - if (Result != OK && Result != NONEMBEDDABLE) - { - ErrorMessage("\nError found\n"); - Result = NOTOK; - break; - } - } - - // Stop the timer - platform_GetTime(end); - - // Finish the count - fprintf(stdout, "%d\n", NumGraphs); - fflush(stdout); - - // Free the graph structures created before the loop - gp_Free(&theGraph); - gp_Free(&origGraph); - - // Print some demographic results - if (Result == OK || Result == NONEMBEDDABLE) - Message("\nNo Errors Found."); - sprintf(Line, "\nDone (%.3lf seconds).\n", platform_GetDuration(start,end)); - Message(Line); - - // Report statistics for planar or outerplanar embedding - if (embedFlags == EMBEDFLAGS_PLANAR || embedFlags == EMBEDFLAGS_OUTERPLANAR) - { - sprintf(Line, "Num Embedded=%d.\n", MainStatistic); - Message(Line); - - for (K=0; K<5; K++) - { - // Outerplanarity does not produces minors C and D - if (embedFlags == EMBEDFLAGS_OUTERPLANAR && (K==2 || K==3)) - continue; - - sprintf(Line, "Minor %c = %d\n", K+'A', ObstructionMinorFreqs[K]); - Message(Line); - } - - if (!(embedFlags & ~EMBEDFLAGS_PLANAR)) - { - sprintf(Line, "\nNote: E1 are added to C, E2 are added to A, and E=E3+E4+K5 homeomorphs.\n"); - Message(Line); - - for (K=5; K max) - { - *pNum = (max + min) / 2; - sprintf(Line, "Number out of range [%d, %d]; changed to %d\n", min, max, *pNum); - ErrorMessage(Line); - } -} - -/**************************************************************************** - MakeGraph() - Internal function that makes a new graph, initializes it, and attaches an - algorithm to it based on the command. - ****************************************************************************/ - -graphP MakeGraph(int Size, char command) -{ - graphP theGraph; - if ((theGraph = gp_New()) == NULL || gp_InitGraph(theGraph, Size) != OK) - { - ErrorMessage("Error creating space for a graph of the given size.\n"); - gp_Free(&theGraph); - return NULL; - } - -// Enable the appropriate feature. Although the same code appears in SpecificGraph, -// it is deliberately not separated to a common utility because SpecificGraph is -// used as a self-contained tutorial. It is not that hard to update both locations -// when new algorithms are added. - - switch (command) - { - case 'd' : gp_AttachDrawPlanar(theGraph); break; - case '2' : gp_AttachK23Search(theGraph); break; - case '3' : gp_AttachK33Search(theGraph); break; - case '4' : gp_AttachK4Search(theGraph); break; - case 'c' : gp_AttachColorVertices(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 if (command == 'c') - { - platform_GetTime(start); - Result = gp_ColorVertices(theGraph); - platform_GetTime(end); - } - else - Result = NOTOK; - - // Write what the algorithm determined and how long it took - WriteAlgorithmResults(theGraph, Result, command, start, end, NULL); - - // On successful algorithm result, write the output file and see if the - // user wants the edge list formatted file. - if (Result == OK || Result == NONEMBEDDABLE) - { - if (outfileName != NULL) - gp_Write(theGraph, outfileName, WRITE_ADJLIST); - - Prompt("Do you want to save the generated graph in edge list format (y/n)? "); - fflush(stdin); - scanf(" %c", &saveEdgeListFormat); - if (tolower(saveEdgeListFormat) == 'y') - { - char *fileName = "maxPlanarEdgeList.txt"; - if (extraEdges > 0) - fileName = "nonPlanarEdgeList.txt"; - - SaveAsciiGraph(theGraph, fileName); - sprintf(Line, "Edge list format saved to '%s'\n", fileName); - Message(Line); - } - } - else ErrorMessage("Failure occurred"); - - gp_Free(&theGraph); - gp_Free(&origGraph); - - FlushConsole(stdout); - return Result; -} diff --git a/planarity_src/c/planaritySpecificGraph.c b/planarity_src/c/planaritySpecificGraph.c deleted file mode 100644 index 203c015..0000000 --- a/planarity_src/c/planaritySpecificGraph.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "planarity.h" - -/**************************************************************************** - SpecificGraph() - ****************************************************************************/ - -int SpecificGraph(char command, char *infileName, char *outfileName, char *outfile2Name) -{ -graphP theGraph, origGraph; -platform_time start, end; -int Result; - - // Get the filename of the graph to test - if ((infileName = ConstructInputFilename(infileName)) == NULL) - return NOTOK; - - // Create the graph and, if needed, attach the correct algorithm to it - theGraph = gp_New(); - - switch (command) - { - case 'd' : gp_AttachDrawPlanar(theGraph); break; - case '2' : gp_AttachK23Search(theGraph); break; - case '3' : gp_AttachK33Search(theGraph); break; - case '4' : gp_AttachK4Search(theGraph); break; - case 'c' : gp_AttachColorVertices(theGraph); break; - } - - // Read the graph into memory - Result = gp_Read(theGraph, infileName); - if (Result == NONEMBEDDABLE) - { - Message("The graph contains too many edges.\n"); - // Some of the algorithms will still run correctly with some edges removed. - if (strchr("pdo234", command)) - { - Message("Some edges were removed, but the algorithm will still run correctly.\n"); - Result = OK; - } - } - - // If there was an unrecoverable error, report it - if (Result != OK) - ErrorMessage("Failed to read graph\n"); - - // Otherwise, call the correct algorithm on it - else - { - // Copy the graph for integrity checking - origGraph = gp_DupGraph(theGraph); - - // Run the algorithm - if (strchr("pdo234", command)) - { - int embedFlags = GetEmbedFlags(command); - platform_GetTime(start); - -// gp_CreateDFSTree(theGraph); -// gp_SortVertices(theGraph); -// gp_Write(theGraph, "debug.before.txt", WRITE_DEBUGINFO); -// gp_SortVertices(theGraph); - - Result = gp_Embed(theGraph, embedFlags); - platform_GetTime(end); - Result = gp_TestEmbedResultIntegrity(theGraph, origGraph, Result); - } - else - { - platform_GetTime(start); - if (command == 'c') - { - if ((Result = gp_ColorVertices(theGraph)) == OK) - Result = gp_ColorVerticesIntegrityCheck(theGraph, origGraph); - } - else - Result = NOTOK; - platform_GetTime(end); - } - - // Write what the algorithm determined and how long it took - WriteAlgorithmResults(theGraph, Result, command, start, end, infileName); - - // Free the graph obtained for integrity checking. - gp_Free(&origGraph); - } - - // Report an error, if there was one, free the graph, and return - if (Result != OK && Result != NONEMBEDDABLE) - { - ErrorMessage("AN ERROR HAS BEEN DETECTED\n"); - Result = NOTOK; -// gp_Write(theGraph, "debug.after.txt", WRITE_DEBUGINFO); - } - - // Provide the output file(s) - else - { - // Restore the vertex ordering of the original graph (undo DFS numbering) - if (strchr("pdo234", command)) - gp_SortVertices(theGraph); - - // Determine the name of the primary output file - outfileName = ConstructPrimaryOutputFilename(infileName, outfileName, command); - - // For some algorithms, the primary output file is not always written - if ((strchr("pdo", command) && Result == NONEMBEDDABLE) || - (strchr("234", command) && Result == OK)) - { - // Do not write the file - } - - // Write the primary output file, if appropriate to do so - else - { - gp_Write(theGraph, outfileName, WRITE_ADJLIST); - } - - // NOW WE WANT TO WRITE THE SECONDARY OUTPUT FILE - - // When called from the menu system, we want to write the planar or outerplanar - // obstruction, if one exists. For planar graph drawing, we want the character - // art rendition. An empty but non-NULL string is passed to indicate the necessity - // of selecting a default name for the second output file. - if (outfile2Name != NULL) - { - if ((command == 'p' || command == 'o') && Result == NONEMBEDDABLE) - { - // By default, use the same name as the primary output filename - if (strlen(outfile2Name) == 0) - outfile2Name = outfileName; - gp_Write(theGraph, outfile2Name, WRITE_ADJLIST); - } - else if (command == 'd' && Result == OK) - { - // By default, add ".render.txt" to the primary output filename - if (strlen(outfile2Name) == 0) - strcat((outfile2Name = outfileName), ".render.txt"); - gp_DrawPlanar_RenderToFile(theGraph, outfile2Name); - } - } - } - - // Free the graph - gp_Free(&theGraph); - - // Flush any remaining message content to the user, and return the result - FlushConsole(stdout); - return Result; -} - -/**************************************************************************** - WriteAlgorithmResults() - ****************************************************************************/ - -void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName) -{ - if (infileName) - sprintf(Line, "The graph '%s' ", infileName); - else sprintf(Line, "The graph "); - Message(Line); - - switch (command) - { - case 'p' : sprintf(Line, "is%s planar.\n", Result==OK ? "" : " not"); break; - case 'd' : sprintf(Line, "is%s planar.\n", Result==OK ? "" : " not"); break; - case 'o' : sprintf(Line, "is%s outerplanar.\n", Result==OK ? "" : " not"); break; - case '2' : sprintf(Line, "has %s subgraph homeomorphic to K_{2,3}.\n", Result==OK ? "no" : "a"); break; - case '3' : sprintf(Line, "has %s subgraph homeomorphic to K_{3,3}.\n", Result==OK ? "no" : "a"); break; - case '4' : sprintf(Line, "has %s subgraph homeomorphic to K_4.\n", Result==OK ? "no" : "a"); break; - case 'c' : sprintf(Line, "has been %d-colored.\n", gp_GetNumColorsUsed(theGraph)); break; - default : sprintf(Line, "nas 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); -} diff --git a/planarity_src/c/planarityUtils.c b/planarity_src/c/planarityUtils.c deleted file mode 100644 index f5478bc..0000000 --- a/planarity_src/c/planarityUtils.c +++ /dev/null @@ -1,378 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "planarity.h" - -/**************************************************************************** - Configuration - ****************************************************************************/ - -char Mode='r', - OrigOut='n', - EmbeddableOut='n', - ObstructedOut='n', - AdjListsForEmbeddingsOut='n', - quietMode='n'; - -void Reconfigure() -{ - fflush(stdin); - - Prompt("\nDo you want to \n" - " Randomly generate graphs (r),\n" - " Specify a graph (s),\n" - " Randomly generate a maximal planar graph (m), or\n" - " Randomly generate a non-planar graph (n)?"); - scanf(" %c", &Mode); - - Mode = tolower(Mode); - if (!strchr("rsmn", Mode)) - Mode = 's'; - - if (Mode == 'r') - { - Message("\nNOTE: The directories for the graphs you want must exist.\n\n"); - - Prompt("Do you want original graphs in directory 'random' (last 10 max)?"); - scanf(" %c", &OrigOut); - - Prompt("Do you want adj. matrix of embeddable graphs in directory 'embedded' (last 10 max))?"); - scanf(" %c", &EmbeddableOut); - - Prompt("Do you want adj. matrix of obstructed graphs in directory 'obstructed' (last 10 max)?"); - scanf(" %c", &ObstructedOut); - - Prompt("Do you want adjacency list format of embeddings in directory 'adjlist' (last 10 max)?"); - scanf(" %c", &AdjListsForEmbeddingsOut); - } - - FlushConsole(stdout); -} - -/**************************************************************************** - MESSAGE - prints a string, but when debugging adds \n and flushes stdout - ****************************************************************************/ - -#define MAXLINE 1024 -char Line[MAXLINE]; - -void Message(char *message) -{ - if (quietMode == 'n') - { - fprintf(stdout, "%s", message); - -#ifdef DEBUG -// fprintf(stdout, "\n"); - fflush(stdout); -#endif - } -} - -void ErrorMessage(char *message) -{ - if (quietMode == 'n') - { - fprintf(stderr, "%s", message); - -#ifdef DEBUG - fprintf(stderr, "\n"); - fflush(stderr); -#endif - } -} - -void FlushConsole(FILE *f) -{ -#ifdef DEBUG - // Certain debuggers only flush completed lines of output to the console - fprintf(f, "\n"); -#endif - fflush(f); -} - -void Prompt(char *message) -{ - Message(message); - FlushConsole(stdout); -} - -/**************************************************************************** - ****************************************************************************/ - -void SaveAsciiGraph(graphP theGraph, char *filename) -{ - int e, EsizeOccupied; - FILE *outfile = fopen(filename, "wt"); - fprintf(outfile, "%s\n", filename); - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2) - { - // Skip the edge holes - if (gp_EdgeInUse(theGraph, e)) - { - fprintf(outfile, "%d %d\n", gp_GetNeighbor(theGraph, e)+1, gp_GetNeighbor(theGraph, e+1)+1); - } - } - - fprintf(outfile, "0 0\n"); - - fclose(outfile); -} - -/**************************************************************************** - ****************************************************************************/ - -int FilesEqual(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; - case 'c' : algorithmName = COLORVERTICES_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; - case 'c' : gp_AttachColorVertices(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; -} diff --git a/planarity_src/c/platformTime.h b/planarity_src/c/platformTime.h deleted file mode 100644 index fe79a9f..0000000 --- a/planarity_src/c/platformTime.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef PLATFORM_TIME -#define PLATFORM_TIME - -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#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 diff --git a/planarity_src/c/stack.c b/planarity_src/c/stack.c deleted file mode 100644 index 75583fb..0000000 --- a/planarity_src/c/stack.c +++ /dev/null @@ -1,232 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#include "appconst.h" -#include "stack.h" -#include - -stackP sp_New(int capacity) -{ -stackP theStack; - - theStack = (stackP) malloc(sizeof(stack)); - - if (theStack != NULL) - { - theStack->S = (int *) malloc(capacity*sizeof(int)); - if (theStack->S == NULL) - { - free(theStack); - theStack = NULL; - } - } - - if (theStack != NULL) - { - theStack->capacity = capacity; - sp_ClearStack(theStack); - } - - return theStack; -} - -void sp_Free(stackP *pStack) -{ - if (pStack == NULL || *pStack == NULL) return; - - (*pStack)->capacity = (*pStack)->size = 0; - - if ((*pStack)->S != NULL) - free((*pStack)->S); - (*pStack)->S = NULL; - free(*pStack); - - *pStack = NULL; -} - -int sp_CopyContent(stackP stackDst, stackP stackSrc) -{ - if (stackDst->capacity < stackSrc->size) - return NOTOK; - - if (stackSrc->size > 0) - memcpy(stackDst->S, stackSrc->S, stackSrc->size*sizeof(int)); - - stackDst->size = stackSrc->size; - return OK; -} - -stackP sp_Duplicate(stackP theStack) -{ -stackP newStack = sp_New(theStack->capacity); - - if (newStack == NULL) - return NULL; - - if (theStack->size > 0) - { - memcpy(newStack->S, theStack->S, theStack->size*sizeof(int)); - newStack->size = theStack->size; - } - - return newStack; -} - -int sp_Copy(stackP stackDst, stackP stackSrc) -{ - if (sp_CopyContent(stackDst, stackSrc) != OK) - { - stackP newStack = sp_Duplicate(stackSrc); - int *p; - - if (newStack == NULL) - return NOTOK; - - p = stackDst->S; - stackDst->S = newStack->S; - newStack->S = p; - newStack->capacity = stackDst->capacity; - sp_Free(&newStack); - - stackDst->size = stackSrc->size; - stackDst->capacity = stackSrc->capacity; - } - - return OK; -} - -#ifndef SPEED_MACROS - -int sp_ClearStack(stackP theStack) -{ - theStack->size = 0; - return OK; -} - -int sp_GetCurrentSize(stackP theStack) -{ - return theStack->size; -} - -int sp_SetCurrentSize(stackP theStack, int size) -{ - return size > theStack->capacity ? NOTOK : (theStack->size = size, OK); -} - -int sp_IsEmpty(stackP theStack) -{ - return !theStack->size; -} - -int sp_NonEmpty(stackP theStack) -{ - return theStack->size; -} - -int sp__Push(stackP theStack, int a) -{ - if (theStack->size >= theStack->capacity) - return NOTOK; - - theStack->S[theStack->size++] = a; - return OK; -} - -int sp__Push2(stackP theStack, int a, int b) -{ - if (theStack->size + 1 >= theStack->capacity) - return NOTOK; - - theStack->S[theStack->size++] = a; - theStack->S[theStack->size++] = b; - return OK; -} - -int sp__Pop(stackP theStack, int *pA) -{ - if (theStack->size <= 0) - return NOTOK; - - *pA = theStack->S[--theStack->size]; - return OK; -} - -int sp__Pop2(stackP theStack, int *pA, int *pB) -{ - if (theStack->size <= 1) - return NOTOK; - - *pB = theStack->S[--theStack->size]; - *pA = theStack->S[--theStack->size]; - - return OK; -} - -int sp_Top(stackP theStack) -{ - return theStack->size ? theStack->S[theStack->size-1] : NIL; -} - -int sp_Get(stackP theStack, int pos) -{ - if (theStack == NULL || pos < 0 || pos >= theStack->size) - return NOTOK; - - return (theStack->S[pos]); -} - -int sp_Set(stackP theStack, int pos, int val) -{ - if (theStack == NULL || pos < 0 || pos >= theStack->size) - return NOTOK; - - return (theStack->S[pos] = val); -} - -#endif // not defined SPEED_MACROS diff --git a/planarity_src/c/stack.h b/planarity_src/c/stack.h deleted file mode 100644 index 088fc38..0000000 --- a/planarity_src/c/stack.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -Planarity-Related Graph Algorithms Project -Copyright (c) 1997-2012, John M. Boyer -All rights reserved. Includes a reference implementation of the following: - -* John M. Boyer. "Subgraph Homeomorphism via the Edge Addition Planarity Algorithm". - Journal of Graph Algorithms and Applications, Vol. 16, no. 2, pp. 381-410, 2012. - http://www.jgaa.info/16/268.html - -* John M. Boyer. "A New Method for Efficiently Generating Planar Graph - Visibility Representations". In P. Eades and P. Healy, editors, - Proceedings of the 13th International Conference on Graph Drawing 2005, - Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006. - -* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n) - Planarity by Edge Addition". Journal of Graph Algorithms and Applications, - Vol. 8, No. 3, pp. 241-273, 2004. - http://www.jgaa.info/08/91.html - -* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding, - Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation, - University of Victoria, 2001. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Planarity-Related Graph Algorithms Project 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 OWNER 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. -*/ - -#ifndef STACK_H -#define STACK_H - -#ifdef __cplusplus -extern "C" { -#endif - -// includes mem functions like memcpy -#include - -typedef struct -{ - int *S; - int size, capacity; -} stack; - -typedef stack * stackP; - -stackP sp_New(int); -void sp_Free(stackP *); - -int sp_Copy(stackP, stackP); - -int sp_CopyContent(stackP stackDst, stackP stackSrc); -stackP sp_Duplicate(stackP theStack); - -#define sp_GetCapacity(theStack) (theStack->capacity) - -#ifndef SPEED_MACROS - -int sp_ClearStack(stackP); -int sp_GetCurrentSize(stackP theStack); -int sp_SetCurrentSize(stackP theStack, int top); - -int sp_IsEmpty(stackP); -int sp_NonEmpty(stackP); - -#define sp_Push(theStack, a) { if (sp__Push(theStack, (a)) != OK) return NOTOK; } -#define sp_Push2(theStack, a, b) { if (sp__Push2(theStack, (a), (b)) != OK) return NOTOK; } - -int sp__Push(stackP, int); -int sp__Push2(stackP, int, int); - -#define sp_Pop(theStack, a) { if (sp__Pop(theStack, &(a)) != OK) return NOTOK; } -#define sp_Pop2(theStack, a, b) { if (sp__Pop2(theStack, &(a), &(b)) != OK) return NOTOK; } - -int sp__Pop(stackP, int *); -int sp__Pop2(stackP, int *, int *); - -int sp_Top(stackP); -int sp_Get(stackP, int); -int sp_Set(stackP, int, int); - -#else - -#define sp_ClearStack(theStack) theStack->size=0 -#define sp_GetCurrentSize(theStack) (theStack->size) -#define sp_SetCurrentSize(theStack, Size) ((Size) > theStack->capacity ? NOTOK : (theStack->size = (Size), OK)) - -#define sp_IsEmpty(theStack) !theStack->size -#define sp_NonEmpty(theStack) theStack->size - -#define sp_Push(theStack, a) theStack->S[theStack->size++] = a -#define sp_Push2(theStack, a, b) {sp_Push(theStack, a); sp_Push(theStack, b);} - -#define sp_Pop(theStack, a) a=theStack->S[--theStack->size] -#define sp_Pop2(theStack, a, b) {sp_Pop(theStack, b);sp_Pop(theStack, a);} - -#define sp_Top(theStack) (theStack->size ? theStack->S[theStack->size-1] : NIL) -#define sp_Get(theStack, pos) (theStack->S[pos]) -#define sp_Set(theStack, pos, val) (theStack->S[pos] = val) - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/planarity_src/fetch_planarity.sh b/planarity_src/fetch_planarity.sh old mode 100644 new mode 100755 index 860957b..2f2563f --- a/planarity_src/fetch_planarity.sh +++ b/planarity_src/fetch_planarity.sh @@ -1,11 +1,19 @@ -#!/bin/bash +#!/usr/bin/env bash # -# How the source was downloaded. - -svn checkout http://planarity.googlecode.com/svn/trunk/ planarity-read-only -cd planarity-read-only -patch -p0 < ../planarity.patch -cd ../ -mv planarity-read-only/c . -rm -r planarity-read-only -rm -r c/.cproject c/.project c/.settings/ c/*.orig c/samples \ No newline at end of file +# How the source was downloaded. + +set -euo pipefail +cd "$(dirname "$0")" || exit 1 + +VERSION=Version_4.0.1.0 + +curl -sL https://github.com/graph-algorithms/edge-addition-planarity-suite/archive/refs/tags/$VERSION.zip -o planarity-$VERSION.zip +unzip planarity-$VERSION.zip + +[[ -d c ]] && rm -r c +mv edge-addition-planarity-suite-$VERSION/{c,LICENSE.TXT} . + +rm -r c/{planarityApp,samples,.gdbinit} + +rm -r edge-addition-planarity-suite-$VERSION +rm planarity-$VERSION.zip diff --git a/planarity_src/planarity.patch b/planarity_src/planarity.patch deleted file mode 100644 index ededbf7..0000000 --- a/planarity_src/planarity.patch +++ /dev/null @@ -1,107 +0,0 @@ -diff -rcpN c/appconst.h c.new/appconst.h -*** c/appconst.h 2011-02-14 07:08:53.000000000 -0600 ---- c.new/appconst.h 2011-02-14 07:12:11.000000000 -0600 -*************** extern int debugNOTOK(); -*** 94,105 **** - /* Array indices are used as pointers, and NIL means bad pointer */ - - // This definition is used with 1-based array indexing -! #define NIL 0 -! #define NIL_CHAR 0x00 - - // This definition is used in combination with 0-based array indexing -! //#define NIL -1 -! //#define NIL_CHAR 0xFF - - /* Defines fopen strings for reading and writing text files on PC and UNIX */ - ---- 94,105 ---- - /* Array indices are used as pointers, and NIL means bad pointer */ - - // This definition is used with 1-based array indexing -! //#define NIL 0 -! //#define NIL_CHAR 0x00 - - // This definition is used in combination with 0-based array indexing -! #define NIL -1 -! #define NIL_CHAR 0xFF - - /* Defines fopen strings for reading and writing text files on PC and UNIX */ - -diff -rcpN c/graphColorVertices.c c.new/graphColorVertices.c -*** c/graphColorVertices.c 2011-02-14 07:08:53.000000000 -0600 ---- c.new/graphColorVertices.c 2011-02-14 07:11:13.000000000 -0600 -*************** extern int COLORVERTICES_ID; -*** 50,56 **** - #include "graph.h" - - #include -! #include - #include - - extern void _ClearVertexVisitedFlags(graphP theGraph, int); ---- 50,56 ---- - #include "graph.h" - - #include -! #include - #include - - extern void _ClearVertexVisitedFlags(graphP theGraph, int); -diff -rcpN c/graphDrawPlanar.c c.new/graphDrawPlanar.c -*** c/graphDrawPlanar.c 2011-02-14 07:08:53.000000000 -0600 ---- c.new/graphDrawPlanar.c 2011-02-14 07:11:13.000000000 -0600 -*************** extern int DRAWPLANAR_ID; -*** 50,56 **** - #include "graph.h" - - #include -! #include - #include - - extern void _ClearVisitedFlags(graphP theGraph); ---- 50,56 ---- - #include "graph.h" - - #include -! #include - #include - - extern void _ClearVisitedFlags(graphP theGraph); -diff -rcpN c/nauty/nauty.h c.new/nauty/nauty.h -*** c/nauty/nauty.h 2011-02-14 07:08:52.000000000 -0600 ---- c.new/nauty/nauty.h 2011-02-14 07:11:13.000000000 -0600 -*************** -*** 16,22 **** - #define SYS_VAXBSD 0 /* older BSD unix 4.2 on a VAX */ - #define SYS_CRAY 0 /* Cray UNIX, portable or standard C */ - #define SYS_APOLLO 0 /* DOMAIN C on Apollo */ -! #define SYS_BSDUNIX 0 /* other BSD unix (cc or gcc compilers) */ - #define SYS_UNIX 0 /* miscellaneous non-BSD unix, including A/UX */ - #define SYS_ALPHA 0 /* Alpha UNIX */ - #define SYS_VAXVMS 0 /* VAX11C or GCC on a VAX under VMS */ ---- 16,22 ---- - #define SYS_VAXBSD 0 /* older BSD unix 4.2 on a VAX */ - #define SYS_CRAY 0 /* Cray UNIX, portable or standard C */ - #define SYS_APOLLO 0 /* DOMAIN C on Apollo */ -! #define SYS_BSDUNIX 1 /* other BSD unix (cc or gcc compilers) */ - #define SYS_UNIX 0 /* miscellaneous non-BSD unix, including A/UX */ - #define SYS_ALPHA 0 /* Alpha UNIX */ - #define SYS_VAXVMS 0 /* VAX11C or GCC on a VAX under VMS */ -*************** -*** 27,33 **** - #define SYS_AMIGALC 0 /* Lattice C on a Commodore Amiga */ - #define SYS_AMIGAAZT 0 /* Aztec C on a Commodore Amiga */ - #define SYS_PCMS4 0 /* Microsoft C 4.0 on IBM PC */ -! #define SYS_PCMS5 1 /* Microsoft C 5.1 or Quick C 2.5 on IBM PC */ - #define SYS_PCTURBO 0 /* Turbo C on IBM PC */ - #define SYS_IBMC 0 /* IBM C Set/2 under OS/2 */ - #define SYS_MISC 0 /* anything else */ ---- 27,33 ---- - #define SYS_AMIGALC 0 /* Lattice C on a Commodore Amiga */ - #define SYS_AMIGAAZT 0 /* Aztec C on a Commodore Amiga */ - #define SYS_PCMS4 0 /* Microsoft C 4.0 on IBM PC */ -! #define SYS_PCMS5 0 /* Microsoft C 5.1 or Quick C 2.5 on IBM PC */ - #define SYS_PCTURBO 0 /* Turbo C on IBM PC */ - #define SYS_IBMC 0 /* IBM C Set/2 under OS/2 */ - #define SYS_MISC 0 /* anything else */ diff --git a/planarity_src/planarity.pyx b/planarity_src/planarity.pyx index 16ba5c8..4a70ea3 100644 --- a/planarity_src/planarity.pyx +++ b/planarity_src/planarity.pyx @@ -3,7 +3,7 @@ Wrapper for Boyer's (C) planarity algorithm. """ -cdef extern from "c/graph.h": +cdef extern from "c/graphLib.h": ctypedef struct vertexRec: int link[2] diff --git a/setup.py b/setup.py index 705760f..b20748a 100644 --- a/setup.py +++ b/setup.py @@ -32,10 +32,8 @@ import sage.libs ext_modules = [] except ImportError: - planarity_dir = 'planarity_src/c/' - planarity_ui_sources = glob(planarity_dir + 'planarity*.c') - planarity_sources = [file for file in glob('planarity_src/c/*.c') - if file not in planarity_ui_sources] + planarity_dir = 'planarity_src' + planarity_sources = glob('planarity_src/c/**/*.c', recursive=True) if sys.platform.startswith('win'): extra_compile_args = [