diff --git a/examples/express/write_tests.py b/examples/express/write_tests.py
index 971cad3..a00720d 100644
--- a/examples/express/write_tests.py
+++ b/examples/express/write_tests.py
@@ -30,3 +30,6 @@
planarity.write(edgelist, "stdout", planarity.WRITE_G6)
print("\n")
+print("GraphML: ")
+planarity.write(edgelist, "stdout", planarity.WRITE_GRAPHML)
+print("\n")
\ No newline at end of file
diff --git a/examples/full/demo_specific_graph.py b/examples/full/demo_specific_graph.py
index 60757b4..48240ee 100644
--- a/examples/full/demo_specific_graph.py
+++ b/examples/full/demo_specific_graph.py
@@ -31,6 +31,7 @@ def specific_graph(
WRITE_ADJLIST,
WRITE_ADJMATRIX,
WRITE_G6,
+ WRITE_GRAPHML,
Graph,
)
@@ -65,7 +66,7 @@ def specific_graph(
outdir: parent directory under which to make output directory
command: algorithm command specifier
output_mode: the desired output format, i.e., WRITE_ADJLIST,
- WRITE_ADJMATRIX, or WRITE_G6
+ WRITE_ADJMATRIX, WRITE_G6, or WRITE_GRAPHML
Returns:
OK, NONEMBEDDABLE, or NOTOK based on the embed_result
@@ -78,7 +79,7 @@ def specific_graph(
"""
gp_SetQuietMode(QUIETMODE_NONE)
- if output_mode not in (WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_G6):
+ if output_mode not in (WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_G6, WRITE_GRAPHML):
raise ValueError(
f"Invalid output_mode = {output_mode}"
)
@@ -232,8 +233,8 @@ def print_embed_result(command: str, embed_result: int) -> None:
type=str,
required=False,
default="a",
- help="Desired graph output format: .g6 (g), Adjacency List (a), or "
- "Adjacency Matrix (m). Defaults to 'a'\n",
+ help="Desired graph output format: .g6 (g), .graphml (x), "
+ "Adjacency List (a), or Adjacency Matrix (m). Defaults to 'a'\n",
)
args = parser.parse_args()
@@ -245,14 +246,15 @@ def print_embed_result(command: str, embed_result: int) -> None:
] if args.commands else PLANARITY_ALGORITHM_SPECIFIERS()
output_mode = (WRITE_ADJLIST if args.mode == "a"
- else (WRITE_ADJMATRIX if args.mode == "m"
+ else (WRITE_ADJMATRIX if args.mode == "m"
else (WRITE_G6 if args.mode == "g"
- else None)))
+ else (WRITE_GRAPHML if args.mode == "x"
+ else None))))
if not output_mode:
raise ValueError(
- f"Invalid argument for mode = {args.mode}; only accepted values "
- "are g (.g6), a (Adjacency List), or m (Adjacency Matrix)"
+ f"Invalid argument for mode = {args.mode}; only accepted values are "
+ "g (.g6), x (.graphml), a (Adjacency List), or m (Adjacency Matrix)"
)
for command in commands:
diff --git a/examples/full/sample_files/one_edge.graphml b/examples/full/sample_files/one_edge.graphml
new file mode 100644
index 0000000..7abd2ba
--- /dev/null
+++ b/examples/full/sample_files/one_edge.graphml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/full/sample_files/one_edge.txt b/examples/full/sample_files/one_edge.txt
new file mode 100644
index 0000000..2a39199
--- /dev/null
+++ b/examples/full/sample_files/one_edge.txt
@@ -0,0 +1,3 @@
+N=2
+1: 2 0
+2: 1 0
diff --git a/planarity/__init__.py b/planarity/__init__.py
index 11c35d2..a1cf81a 100644
--- a/planarity/__init__.py
+++ b/planarity/__init__.py
@@ -53,6 +53,7 @@
WRITE_ADJLIST,
WRITE_ADJMATRIX,
WRITE_G6,
+ WRITE_GRAPHML,
GRAPHFLAGS_ZEROBASEDIO,
)
# Surfaced from planarity/c/graphLib/planarityRelated/graphPlanarity.h
diff --git a/planarity/c/LICENSE.TXT b/planarity/c/LICENSE.TXT
index 630ed7a..56b3413 100644
--- a/planarity/c/LICENSE.TXT
+++ b/planarity/c/LICENSE.TXT
@@ -1,26 +1,4 @@
-The Edge Addition Planarity Suite
Copyright (c) 1997-2026, 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:
diff --git a/planarity/c/graphLib/extensionSystem/graphExtensions.c b/planarity/c/graphLib/extensionSystem/graphExtensions.c
index 8088458..b6f5b5a 100644
--- a/planarity/c/graphLib/extensionSystem/graphExtensions.c
+++ b/planarity/c/graphLib/extensionSystem/graphExtensions.c
@@ -8,6 +8,7 @@ See the LICENSE.TXT file for licensing information.
#include
#include "../lowLevelUtils/appconst.h"
+#include "../graph.private.h"
#include "graphExtensions.private.h"
#include "graphExtensions.h"
@@ -95,15 +96,11 @@ static int moduleIDGenerator = 0;
if your feature is attached but not active or if your feature
augments the base behavior rather than replacing it.
- a) If extra data must be maintained at the graph, vertex or edge
- levels, then fpEnsureVertexCapacity(), fpEnsureEdgeCapacity(),
- and fpResetGraphStorage() must be overloaded.
+ a) If extra data must be maintained at the graph, vertex or edge
+ levels, then fpEnsureVertexCapacity(), fpEnsureEdgeCapacity(),
+ fpDeleteEdge(), and fpResetGraphStorage() must be overloaded.
- b) For a parallel array of edge data, the extension must currently
- provides its own _Feature_DeleteEdge() that initializes its edge
- extension data along with calling gp_DeleteEdge().
-
- c) If any data must be persisted in the file format, then overloads
+ b) 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(),
@@ -182,7 +179,12 @@ int gp_AddExtension(graphP theGraph,
if (theGraph == NULL || pModuleID == NULL ||
context == NULL || copyData == NULL || freeContext == NULL ||
- functions == NULL)
+ functions == NULL || theGraph->extensionLookupTable == NULL)
+ {
+ return NOTOK;
+ }
+
+ if (*pModuleID < 0 || *pModuleID > MAXNUMSUPPORTEDEXTENSIONS)
{
return NOTOK;
}
@@ -196,6 +198,10 @@ int gp_AddExtension(graphP theGraph,
// Assign a unique ID to the extension if it does not already have one
if (*pModuleID == 0)
{
+ if (moduleIDGenerator >= MAXNUMSUPPORTEDEXTENSIONS)
+ {
+ return NOTOK;
+ }
*pModuleID = ++moduleIDGenerator;
}
@@ -218,6 +224,7 @@ int gp_AddExtension(graphP theGraph,
// Make the new linkages
newExtension->next = (struct graphExtensionStruct *)theGraph->extensions;
theGraph->extensions = newExtension;
+ theGraph->extensionLookupTable[newExtension->moduleID] = newExtension;
// The new extension was successfully added
return OK;
@@ -273,32 +280,28 @@ void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions)
int gp_FindExtension(graphP theGraph, int moduleID, void **pContext)
{
- graphExtensionP first = NULL, next = NULL;
+ graphExtensionP extension = NULL;
if (pContext != NULL)
{
*pContext = NULL;
}
- if (theGraph == NULL || moduleID == 0)
+ if (theGraph == NULL || moduleID <= 0 ||
+ moduleID > MAXNUMSUPPORTEDEXTENSIONS ||
+ theGraph->extensionLookupTable == NULL)
{
return FALSE;
}
- first = theGraph->extensions;
-
- while (first != NULL)
+ extension = theGraph->extensionLookupTable[moduleID];
+ if (extension != NULL)
{
- next = (graphExtensionP)first->next;
- if (first->moduleID == moduleID)
+ if (pContext != NULL)
{
- if (pContext != NULL)
- {
- *pContext = first->context;
- }
- return TRUE;
+ *pContext = extension->context;
}
- first = next;
+ return TRUE;
}
return FALSE;
@@ -342,7 +345,9 @@ int gp_RemoveExtension(graphP theGraph, int moduleID)
{
graphExtensionP prev = NULL, curr = NULL, next = NULL;
- if (theGraph == NULL || moduleID == 0)
+ if (theGraph == NULL || moduleID <= 0 ||
+ moduleID > MAXNUMSUPPORTEDEXTENSIONS ||
+ theGraph->extensionLookupTable == NULL)
return NOTOK;
curr = theGraph->extensions;
@@ -372,6 +377,7 @@ int gp_RemoveExtension(graphP theGraph, int moduleID)
theGraph->extensions = next;
// Free the curr extension
+ theGraph->extensionLookupTable[curr->moduleID] = NULL;
_FreeExtension(curr);
}
@@ -448,7 +454,8 @@ int gp_DupExtensions(graphP dstGraph, graphP srcGraph)
{
graphExtensionP next = NULL, newNext = NULL, newLast = NULL;
- if (srcGraph == NULL || dstGraph == NULL)
+ if (srcGraph == NULL || dstGraph == NULL ||
+ dstGraph->extensionLookupTable == NULL)
return NOTOK;
gp_FreeExtensions(dstGraph);
@@ -457,6 +464,12 @@ int gp_DupExtensions(graphP dstGraph, graphP srcGraph)
while (next != NULL)
{
+ if (next->moduleID <= 0 || next->moduleID > MAXNUMSUPPORTEDEXTENSIONS)
+ {
+ gp_FreeExtensions(dstGraph);
+ return NOTOK;
+ }
+
if ((newNext = (graphExtensionP)malloc(sizeof(graphExtensionStruct))) == NULL)
{
gp_FreeExtensions(dstGraph);
@@ -471,6 +484,10 @@ int gp_DupExtensions(graphP dstGraph, graphP srcGraph)
newNext->functions = next->functions;
newNext->next = NULL;
+ // Add the new extension to the dstGraph extension lookup table
+ dstGraph->extensionLookupTable[newNext->moduleID] = newNext;
+
+ // Add the new extension to the dstGraph extensions linked list
if (newLast != NULL)
newLast->next = (struct graphExtensionStruct *)newNext;
else
@@ -488,38 +505,18 @@ int gp_DupExtensions(graphP dstGraph, graphP srcGraph)
gp_CopyExtensions()
********************************************************************/
-// This number can just be made bigger if ever needed
-#define MAXNUMSUPPORTEDEXTENSIONS 32
-
int gp_CopyExtensions(graphP dstGraph, graphP srcGraph)
{
graphExtensionP dstExtension = NULL, srcExtension = NULL;
- graphExtensionP srcExtensionIDMap[MAXNUMSUPPORTEDEXTENSIONS+1];
- if (srcGraph == NULL || dstGraph == NULL)
+ if (srcGraph == NULL || dstGraph == NULL ||
+ srcGraph->extensionLookupTable == NULL ||
+ dstGraph->extensionLookupTable == NULL)
return NOTOK;
if (gp_GetN(dstGraph) != gp_GetN(srcGraph) || gp_GetN(dstGraph) == 0)
return NOTOK;
- // NULL out the pointers in the srcExtensionIDMap
- memset(srcExtensionIDMap, 0, (MAXNUMSUPPORTEDEXTENSIONS+1)*sizeof(graphExtensionP));
-
- // Run through the srcGraph extensions linked list and put the pointer to
- // each extension in the srcExtensionIDMap at the index location indicated
- // by the extension module ID. This mapping is needed because the srcGraph
- // may have been extended with the extensions in a different order than the
- // dstGraph, but the moduleID for each extension is the same across graphs.
- srcExtension = srcGraph->extensions;
- while (srcExtension != NULL)
- {
- if (srcExtension->moduleID < 0 || srcExtension->moduleID > MAXNUMSUPPORTEDEXTENSIONS)
- return NOTOK;
-
- srcExtensionIDMap[srcExtension->moduleID] = srcExtension;
- srcExtension = (graphExtensionP)srcExtension->next;
- }
-
// For each extension in the dstGraph, if the srcGraph has the same extension,
// then we invoke the extension's copyData to copy from srcGraph to dstGraph.
// If the dstGraph has an extension that the srcGraph does not have, then
@@ -528,7 +525,11 @@ int gp_CopyExtensions(graphP dstGraph, graphP srcGraph)
dstExtension = dstGraph->extensions;
while (dstExtension != NULL)
{
- srcExtension = srcExtensionIDMap[dstExtension->moduleID];
+ if (dstExtension->moduleID <= 0 ||
+ dstExtension->moduleID > MAXNUMSUPPORTEDEXTENSIONS)
+ return NOTOK;
+
+ srcExtension = srcGraph->extensionLookupTable[dstExtension->moduleID];
if (dstExtension->copyData(dstExtension->context, srcExtension ? srcExtension->context : NULL) != OK)
return NOTOK;
@@ -562,6 +563,10 @@ void gp_FreeExtensions(graphP theGraph)
}
theGraph->extensions = NULL;
+ if (theGraph->extensionLookupTable != NULL)
+ {
+ memset(theGraph->extensionLookupTable, 0, (MAXNUMSUPPORTEDEXTENSIONS+1)*sizeof(graphExtensionP));
+ }
_InitFunctionTable(theGraph);
}
}
diff --git a/planarity/c/graphLib/extensionSystem/graphExtensions.h b/planarity/c/graphLib/extensionSystem/graphExtensions.h
index 5cfb5a0..9a215eb 100644
--- a/planarity/c/graphLib/extensionSystem/graphExtensions.h
+++ b/planarity/c/graphLib/extensionSystem/graphExtensions.h
@@ -17,9 +17,9 @@ extern "C"
int gp_AddExtension(graphP theGraph,
int *pModuleID,
void *context,
- void *(*dupContext)(void *, void *),
- int (*copyData)(void *, void *),
- void (*freeContext)(void *),
+ void *(*dupContext)(void *pContext, void *theGraph),
+ int (*copyData)(void *dstContext, void *srcContext),
+ void (*freeContext)(void *pContext),
graphFunctionTableP overloadTable);
int gp_FindExtension(graphP theGraph, int moduleID, void **pContext);
diff --git a/planarity/c/graphLib/extensionSystem/graphExtensions.private.h b/planarity/c/graphLib/extensionSystem/graphExtensions.private.h
index c993ecc..144f654 100644
--- a/planarity/c/graphLib/extensionSystem/graphExtensions.private.h
+++ b/planarity/c/graphLib/extensionSystem/graphExtensions.private.h
@@ -18,9 +18,9 @@ extern "C"
{
int moduleID;
void *context;
- void *(*dupContext)(void *, void *);
- int (*copyData)(void *, void *);
- void (*freeContext)(void *);
+ void *(*dupContext)(void *pContext, void *theGraph);
+ int (*copyData)(void *dstContext, void *srcContext);
+ void (*freeContext)(void *pContext);
graphFunctionTableP functions;
diff --git a/planarity/c/graphLib/extensionSystem/graphFunctionTable.h b/planarity/c/graphLib/extensionSystem/graphFunctionTable.h
index 1b5b3e4..14e04ac 100644
--- a/planarity/c/graphLib/extensionSystem/graphFunctionTable.h
+++ b/planarity/c/graphLib/extensionSystem/graphFunctionTable.h
@@ -24,36 +24,37 @@ extern "C"
// 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 (*fpEmbeddingInitialize)(graphP theGraph);
+ void (*fpEmbedBackEdgeToDescendant)(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
+ void (*fpWalkUp)(graphP theGraph, int v, int e);
+ int (*fpWalkDown)(graphP theGraph, int v, int RootVertex);
+ int (*fpMergeBicomps)(graphP theGraph, int v, int RootVertex, int W, int WPrevLink);
+ void (*fpMergeVertex)(graphP theGraph, int W, int WPrevLink, int R);
+ int (*fpHandleInactiveVertex)(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink);
+ int (*fpHandleBlockedBicomp)(graphP theGraph, int v, int RootVertex, int R);
+ int (*fpEmbedPostprocess)(graphP theGraph, int v, int edgeEmbeddingResult);
+ int (*fpMarkDFSPath)(graphP theGraph, int ancestor, int descendant);
- int (*fpCheckEmbeddingIntegrity)(graphP, graphP);
- int (*fpCheckObstructionIntegrity)(graphP, graphP);
+ int (*fpCheckEmbeddingIntegrity)(graphP theGraph, graphP origGraph);
+ int (*fpCheckObstructionIntegrity)(graphP theGraph, graphP origGraph);
// These function pointers allow extension modules to overload some
// of the behaviors of gp_* function in the public API
- int (*fpEnsureVertexCapacity)(graphP, int);
- void (*fpResetGraphStorage)(graphP);
- int (*fpEnsureEdgeCapacity)(graphP, int);
- int (*fpSortVertices)(graphP);
+ int (*fpEnsureVertexCapacity)(graphP theGraph, int N);
+ void (*fpResetGraphStorage)(graphP theGraph);
+ int (*fpEnsureEdgeCapacity)(graphP theGraph, int requiredEdgeCapacity);
+ int (*fpSortVertices)(graphP theGraph);
- int (*fpReadPostprocess)(graphP, char *);
- int (*fpWritePostprocess)(graphP, char **);
+ int (*fpReadPostprocess)(graphP theGraph, char *extraData);
+ int (*fpWritePostprocess)(graphP theGraph, char **pExtraData);
- 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);
+ int (*fpDeleteEdge)(graphP theGraph, int e);
+ void (*fpHideEdge)(graphP theGraph, int e);
+ void (*fpRestoreEdge)(graphP theGraph, int e);
+ int (*fpHideVertex)(graphP theGraph, int vertex);
+ int (*fpRestoreVertex)(graphP theGraph);
+ int (*fpContractEdge)(graphP theGraph, int e);
+ int (*fpIdentifyVertices)(graphP theGraph, int u, int v, int eBefore);
};
typedef struct graphFunctionTableStruct graphFunctionTableStruct;
diff --git a/planarity/c/graphLib/graph.c b/planarity/c/graphLib/graph.c
index e0dd81d..d1db764 100644
--- a/planarity/c/graphLib/graph.c
+++ b/planarity/c/graphLib/graph.c
@@ -53,7 +53,8 @@ void _ClearEdgeVisitedFlags(graphP theGraph);
int _ClearAllVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
int _ClearAllVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
void _ClearEdgeVisitedFlagsInUnembeddedEdges(graphP theGraph);
-int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue);
+int _FillVertexVisitedIndexes(graphP theGraph, int FillValue);
+int _FillVertexVisitedIndexesInBicomp(graphP theGraph, int BicompRoot, int FillValue);
int _ClearObstructionMarksInBicomp(graphP theGraph, int BicompRoot);
int _gp_FindEdge(graphP theGraph, int u, int v);
@@ -83,14 +84,28 @@ void _InitEdges(graphP theGraph);
void _ClearGraph(graphP theGraph);
-int _GetRandomNumber(int NMin, int NMax);
+typedef struct
+{
+ int u;
+ int v;
+} randomGraphEdgeRec;
+
+typedef struct
+{
+ int a;
+ int b;
+ int c;
+} randomGraphFaceRec;
-int _getUnprocessedChild(graphP theGraph, int parent);
-int _hasUnprocessedChild(graphP theGraph, int parent);
+int _AddRandomGraphEdgeCandidate(randomGraphEdgeRec *edgeList, int edgeListCapacity, int *pEdgeListCount, int u, int v);
+int _ProcessRandomGraphOptionalEdge(graphP theGraph, randomGraphEdgeRec *edgeList, int edgeListCapacity,
+ int *pEdgeListCount, int addImmediately, int u, int v);
+void _ShuffleRandomGraphEdgeCandidates(randomGraphEdgeRec *edgeList, int edgeCount);
void _AttachEdgeRecord(graphP theGraph, int v, int e, int link, int newEdge);
void _DetachEdgeRecord(graphP theGraph, int e);
void _RestoreEdgeRecord(graphP theGraph, int e);
+int _DeleteEdge(graphP theGraph, int e);
/* Private functions for which there are FUNCTION POINTERS */
@@ -113,10 +128,13 @@ graphP gp_New(void)
graphP theGraph = (graphP)calloc(1, sizeof(graphStruct));
graphFunctionTableP functionTable = (graphFunctionTableP)calloc(1, sizeof(graphFunctionTableStruct));
graphPrivateDataP theGraphPrivateData = (graphPrivateDataP)calloc(1, sizeof(graphPrivateDataStruct));
+ graphExtensionP *extensionLookupTable = (graphExtensionP *)calloc(MAXNUMSUPPORTEDEXTENSIONS + 1, sizeof(graphExtensionP));
- if (theGraph != NULL && functionTable != NULL && theGraphPrivateData != NULL)
+ if (theGraph != NULL && functionTable != NULL &&
+ theGraphPrivateData != NULL && extensionLookupTable != NULL)
{
theGraph->privateData = (void *)theGraphPrivateData;
+ theGraph->extensionLookupTable = extensionLookupTable;
theGraph->functions = functionTable;
_InitFunctionTable(theGraph);
@@ -140,6 +158,11 @@ graphP gp_New(void)
free(theGraphPrivateData);
theGraphPrivateData = NULL;
}
+ if (extensionLookupTable != NULL)
+ {
+ free(extensionLookupTable);
+ extensionLookupTable = NULL;
+ }
}
return theGraph;
@@ -184,6 +207,7 @@ void _InitFunctionTable(graphP theGraph)
theGraph->functions->fpReadPostprocess = _ReadPostprocess;
theGraph->functions->fpWritePostprocess = _WritePostprocess;
+ theGraph->functions->fpDeleteEdge = _DeleteEdge;
theGraph->functions->fpHideEdge = _HideEdge;
theGraph->functions->fpRestoreEdge = _RestoreEdge;
theGraph->functions->fpHideVertex = _HideVertex;
@@ -225,13 +249,15 @@ void _InitFunctionTable(graphP theGraph)
An instance of the isolator context is created.
- Returns OK on success, NOTOK on aany failure.
+ Returns OK on success, NOTOK on any failure.
On NOTOK, graph extensions are freed so that the graph is
returned to the post-condition of gp_New().
********************************************************************/
int gp_EnsureVertexCapacity(graphP theGraph, int N)
{
+ long long effectiveEdgeCapacity = 0;
+
// valid params check
if (theGraph == NULL || N <= 0)
return NOTOK;
@@ -240,6 +266,21 @@ int gp_EnsureVertexCapacity(graphP theGraph, int N)
if (gp_GetN(theGraph) > 0)
return NOTOK;
+ // Reject a vertex count whose capacity arithmetic cannot be represented
+ // in int (issue #325). The effective edge capacity is the greater of a
+ // pre-set edgeCapacity and DEFAULT_EDGE_CAPACITY_FACTOR * N, because
+ // gp_EnsureEdgeCapacity() may legitimately have stored a lower value
+ // before this call. The stack holds (edgeCapacity << 2) + 2 entries,
+ // which strictly exceeds every other derived quantity (vertex storage,
+ // edge storage and the 2 * 2 * DEFAULT_EDGE_CAPACITY_FACTOR * N + 2
+ // fallback), so one test on it shields them all, including the
+ // gp_UpperBoundEdgeStorage() uses in the algorithm extensions.
+ effectiveEdgeCapacity = (long long)DEFAULT_EDGE_CAPACITY_FACTOR * N;
+ if (effectiveEdgeCapacity < theGraph->edgeCapacity)
+ effectiveEdgeCapacity = theGraph->edgeCapacity;
+ if ((effectiveEdgeCapacity << 2) + 2 > INT_MAX)
+ return NOTOK;
+
return theGraph->functions->fpEnsureVertexCapacity(theGraph, N);
}
@@ -343,8 +384,10 @@ void _ResetGraphStorage(graphP theGraph)
theGraph->embedFlags = 0;
theGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED;
+ theGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED_DIRECTED;
theGraph->graphFlags &= ~GRAPHFLAGS_SORTEDBYDFI;
-
+ theGraph->graphFlags &= ~GRAPHFLAGS_LOWPOINTSCOMPUTED;
+ theGraph->graphFlags &= ~GRAPHFLAGS_DIRECTEDEDGEDETECTED;
_InitVertices(theGraph);
_InitEdges(theGraph);
_InitIsolatorContext(theGraph);
@@ -400,6 +443,15 @@ int gp_EnsureEdgeCapacity(graphP theGraph, int requiredEdgeCapacity)
if (theGraph == NULL || requiredEdgeCapacity <= 0)
return NOTOK;
+ // Reject a capacity whose stack arithmetic cannot be represented in int
+ // (issue #325). Together with the guard in gp_EnsureVertexCapacity(),
+ // this keeps every stored capacity able to support its stack and edge
+ // storage arithmetic: the vertex-side guard covers the derived
+ // quantities computed from N, and this test covers the ones computed
+ // from a stored or requested capacity
+ if ((((long long)requiredEdgeCapacity) << 2) + 2 > INT_MAX)
+ return NOTOK;
+
if (theGraph->edgeCapacity >= requiredEdgeCapacity)
return OK;
@@ -434,7 +486,7 @@ int _EnsureEdgeCapacity(graphP theGraph, int requiredEdgeCapacity)
{
int newStackSize = 2 * (2 * requiredEdgeCapacity) + 2;
- if (newStackSize < 2 * DEFAULT_EDGE_CAPACITY_FACTOR * gp_GetN(theGraph) + 2)
+ if (newStackSize < 2 * (2 * DEFAULT_EDGE_CAPACITY_FACTOR * gp_GetN(theGraph)) + 2)
{
// NOTE: We enforce a minimum stack based on number of vertices
// if edgeCapacity is small. Currently, this will not
@@ -442,7 +494,7 @@ int _EnsureEdgeCapacity(graphP theGraph, int requiredEdgeCapacity)
// the capacity can only ever get bigger. However, this
// rule is enforced in case future methods are added
// that reduce edge capacity
- newStackSize = 2 * DEFAULT_EDGE_CAPACITY_FACTOR * gp_GetN(theGraph) + 2;
+ newStackSize = 2 * (2 * DEFAULT_EDGE_CAPACITY_FACTOR * gp_GetN(theGraph)) + 2;
}
if ((newStack = sp_New(newStackSize)) == NULL)
@@ -502,7 +554,7 @@ void _InitVertexInfo(graphP theGraph, int v)
gp_SetVertexLeastAncestor(theGraph, v, NIL);
gp_SetVertexLowpoint(theGraph, v, NIL);
- gp_SetVertexVisitedInfo(theGraph, v, NIL);
+ gp_SetVertexVisitedIndex(theGraph, v, NIL);
gp_SetVertexPertinentEdge(theGraph, v, NIL);
gp_SetVertexPertinentRootsList(theGraph, v, NIL);
gp_SetVertexFuturePertinentChild(theGraph, v, NIL);
@@ -680,6 +732,9 @@ int _ClearAllVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x)
{
int e, eTwin;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)w;
+
// 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_FindEdge(theGraph, v, u);
@@ -722,6 +777,9 @@ int _SetAllVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x)
{
int e, eTwin;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)w;
+
// 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_FindEdge(theGraph, v, u);
@@ -751,9 +809,29 @@ int _SetAllVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x)
}
/********************************************************************
- _FillVertexVisitedInfoInBicomp()
+ _FillVertexVisitedIndexes()
+
+ Places the FillValue into the visitedIndex of all non-virtual vertices
+ in the graph.
- Places the FillValue into the visitedInfo of the non-virtual vertices
+ Returns OK on success, NOTOK on failure.
+ ********************************************************************/
+
+int _FillVertexVisitedIndexes(graphP theGraph, int FillValue)
+{
+ if (theGraph == NULL)
+ return NOTOK;
+
+ for (int v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ gp_SetVertexVisitedIndex(theGraph, v, FillValue);
+
+ return OK;
+}
+
+/********************************************************************
+ _FillVertexVisitedIndexesInBicomp()
+
+ Places the FillValue into the visitedIndex of the non-virtual vertices
in the bicomp rooted by BicompRoot.
This method uses the stack but preserves whatever may have been
@@ -763,7 +841,7 @@ int _SetAllVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x)
Returns OK on success, NOTOK on implementation failure.
********************************************************************/
-int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue)
+int _FillVertexVisitedIndexesInBicomp(graphP theGraph, int BicompRoot, int FillValue)
{
int v, e;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
@@ -774,7 +852,7 @@ int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValu
sp_Pop(theGraph->theStack, v);
if (gp_IsNotVirtualVertex(theGraph, v))
- gp_SetVertexVisitedInfo(theGraph, v, FillValue);
+ gp_SetVertexVisitedIndex(theGraph, v, FillValue);
e = gp_GetFirstEdge(theGraph, v);
while (gp_IsEdge(theGraph, e))
@@ -918,6 +996,11 @@ void gp_Free(graphP *pGraph)
free((*pGraph)->privateData);
(*pGraph)->privateData = NULL;
}
+ if ((*pGraph)->extensionLookupTable != NULL)
+ {
+ free((*pGraph)->extensionLookupTable);
+ (*pGraph)->extensionLookupTable = NULL;
+ }
free(*pGraph);
*pGraph = NULL;
@@ -968,6 +1051,14 @@ int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph)
sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles);
dstGraph->numEdgeHoles = sp_GetCurrentSize(dstGraph->edgeHoles);
+ dstGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED;
+ dstGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED_DIRECTED;
+ dstGraph->graphFlags &= ~GRAPHFLAGS_SORTEDBYDFI;
+ dstGraph->graphFlags &= ~GRAPHFLAGS_LOWPOINTSCOMPUTED;
+ dstGraph->graphFlags &= ~GRAPHFLAGS_DIRECTEDEDGEDETECTED;
+ if (gp_GetGraphFlags(srcGraph) & GRAPHFLAGS_DIRECTEDEDGEDETECTED)
+ dstGraph->graphFlags |= GRAPHFLAGS_DIRECTEDEDGEDETECTED;
+
return OK;
}
@@ -1135,6 +1226,12 @@ graphP gp_DupGraph(graphP theGraph)
int gp_CreateRandomGraph(graphP theGraph)
{
int N, M, u, v, m;
+ long long maxSimpleEdges;
+
+ if (theGraph == NULL)
+ {
+ return NOTOK;
+ }
N = gp_GetN(theGraph);
@@ -1145,7 +1242,7 @@ int gp_CreateRandomGraph(graphP theGraph)
for (v = gp_LowerBoundVertices(theGraph) + 1; v < gp_UpperBoundVertices(theGraph); ++v)
{
- u = _GetRandomNumber(gp_LowerBoundVertices(theGraph), v - 1);
+ u = gp_GetRandomNumber(gp_LowerBoundVertices(theGraph), v - 1);
if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
return NOTOK;
}
@@ -1154,15 +1251,21 @@ int gp_CreateRandomGraph(graphP theGraph)
(actually, leave open a small chance that no
additional edges will be added). */
- M = _GetRandomNumber(7 * N / 8, theGraph->edgeCapacity);
+ M = gp_GetRandomNumber(7 * N / 8, theGraph->edgeCapacity);
+
+ /* N * (N - 1) overflows a 32-bit int at N == 46342, before the halved
+ result would, so the simple undirected edge bound is formed in a
+ wider type. */
- if (M > N * (N - 1) / 2)
- M = N * (N - 1) / 2;
+ maxSimpleEdges = ((long long)N * (N - 1)) / 2;
+
+ if (M > maxSimpleEdges)
+ M = (int)maxSimpleEdges;
for (m = N - 1; m < M; m++)
{
- u = _GetRandomNumber(gp_LowerBoundVertices(theGraph), gp_UpperBoundVertices(theGraph) - 2);
- v = _GetRandomNumber(u + 1, gp_UpperBoundVertices(theGraph) - 1);
+ u = gp_GetRandomNumber(gp_LowerBoundVertices(theGraph), gp_UpperBoundVertices(theGraph) - 2);
+ v = gp_GetRandomNumber(u + 1, gp_UpperBoundVertices(theGraph) - 1);
// If the edge (u,v) exists, decrement eIndex to try again
if (gp_IsNeighbor(theGraph, u, v))
@@ -1179,98 +1282,45 @@ int gp_CreateRandomGraph(graphP theGraph)
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 _AddRandomGraphEdgeCandidate(randomGraphEdgeRec *edgeList, int edgeListCapacity, int *pEdgeListCount, int u, int v)
{
- int N = rand();
+ if (edgeList == NULL || pEdgeListCount == NULL || *pEdgeListCount >= edgeListCapacity)
+ return NOTOK;
- if (NMax < NMin)
- return NMin;
+ edgeList[*pEdgeListCount].u = u;
+ edgeList[*pEdgeListCount].v = v;
+ (*pEdgeListCount)++;
- N += ((N & 0xFFFF0000) >> 16);
- N += ((N & 0x0000FF00) >> 8);
- N &= 0x7FFFFFF;
- N %= (NMax - NMin + 1);
- return N + NMin;
+ return OK;
}
-/********************************************************************
- _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 _ProcessRandomGraphOptionalEdge(graphP theGraph, randomGraphEdgeRec *edgeList, int edgeListCapacity,
+ int *pEdgeListCount, int addImmediately, int u, int v)
{
- int e = gp_GetFirstEdge(theGraph, parent);
- int eTwin = gp_GetTwin(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 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;
+ if (addImmediately)
+ return gp_AddEdge(theGraph, u, 0, v, 0) == OK ? OK : NOTOK;
- // 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);
+ if (edgeList != NULL)
+ return _AddRandomGraphEdgeCandidate(edgeList, edgeListCapacity, pEdgeListCount, u, v);
- // Now we move the edge record in the parent vertex to the end
- // of the adjacency list of that vertex.
- gp_MoveEdgeToLast(theGraph, parent, e);
-
- // Now we move the edge record in the child vertex to the
- // end of the adjacency list of the child.
- gp_MoveEdgeToLast(theGraph, child, eTwin);
-
- // Now we set the child's parent and return the child.
- gp_SetVertexParent(theGraph, child, parent);
-
- return child;
+ return OK;
}
-/********************************************************************
- _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)
+void _ShuffleRandomGraphEdgeCandidates(randomGraphEdgeRec *edgeList, int edgeCount)
{
- int e = gp_GetFirstEdge(theGraph, parent);
+ int e;
- if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED)
- return 0;
+ if (edgeList == NULL)
+ return;
- if (gp_GetEdgeVisited(theGraph, e))
- return 0;
+ for (e = edgeCount - 1; e > 0; --e)
+ {
+ int e2 = gp_GetRandomNumber(0, e);
+ randomGraphEdgeRec temp = edgeList[e];
- return 1;
+ edgeList[e] = edgeList[e2];
+ edgeList[e2] = temp;
+ }
}
/********************************************************************
@@ -1284,18 +1334,27 @@ int _hasUnprocessedChild(graphP theGraph, int parent)
of random additional edges. These cases correspond to the
numEdges being equal to 3N-6 or greater than 3N-6, respectively.
- If numEdges < 3N-6, then the graph generated is a random tree plus
- edges added systematically to the tree while maintaining planarity.
- The output graph will have at least numEdges edges, but it may have
- a few more since more than one edge is added per iteration of the
- loop that adds the extra edges to the random tree.
+ If numEdges < 3N-6, then the graph generated is a connected planar
+ subgraph of a random maximal planar graph. The output graph will
+ have exactly numEdges edges.
- This function assumes the caller has already called srand().
+ NOTE: This function assumes the caller has already called srand().
+
+ NOTE: If numEdges is larger than the edge capacity of theGraph, then
+ then its value is reduced internally. The caller can invoke
+ gp_EnsureEdgeCapacity() beforehand, if desired.
********************************************************************/
int gp_CreateRandomGraphEx(graphP theGraph, int numEdges)
{
- int N, M, root, v, c, p, last, u, e;
+ randomGraphEdgeRec *optionalEdges = NULL;
+ randomGraphFaceRec *faces = NULL;
+ int N, maxPlanarEdges, numPlanarCoreEdges;
+ long long maxNumEdges;
+ int lowerVertex, upperVertex, faceCapacity, optionalEdgeCapacity;
+ int optionalEdgeCount = 0, faceCount = 0, addAllPlanarEdges;
+ int Result = OK;
+ int v, u, e;
// Parameter checks: Must have a graph of at least three vertices, and the
// number of edges must be at least enough to support making a random tree.
@@ -1303,215 +1362,161 @@ int gp_CreateRandomGraphEx(graphP theGraph, int numEdges)
return NOTOK;
N = gp_GetN(theGraph);
+ lowerVertex = gp_LowerBoundVertices(theGraph);
+ upperVertex = gp_UpperBoundVertices(theGraph);
+ // Formed in a wider type because N * (N - 1) overflows a 32-bit int at
+ // N == 46342, before the halved result would.
+ maxNumEdges = ((long long)N * (N - 1)) >> 1;
+ maxPlanarEdges = 3 * N - 6;
if (numEdges > theGraph->edgeCapacity)
numEdges = theGraph->edgeCapacity;
- /* Generate a random tree. */
+ if (numEdges > maxNumEdges)
+ numEdges = (int)maxNumEdges;
- for (v = gp_LowerBoundVertices(theGraph) + 1; v < gp_UpperBoundVertices(theGraph); ++v)
- {
- u = _GetRandomNumber(gp_LowerBoundVertices(theGraph), v - 1);
- if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
- return NOTOK;
+ if (numEdges < N - 1)
+ return NOTOK;
- else
- {
- e = _gp_FindEdge(theGraph, u, v);
- gp_SetEdgeType(theGraph, e, EDGE_TYPE_TREE);
- gp_SetEdgeType(theGraph, gp_GetTwin(theGraph, e), EDGE_TYPE_TREE);
- gp_ClearEdgeVisited(theGraph, e);
- gp_ClearEdgeVisited(theGraph, gp_GetTwin(theGraph, e));
- }
- }
+ numPlanarCoreEdges = numEdges <= maxPlanarEdges ? numEdges : maxPlanarEdges;
+ addAllPlanarEdges = numPlanarCoreEdges == maxPlanarEdges;
+ faceCapacity = 2 * N - 4;
+ optionalEdgeCapacity = maxPlanarEdges - (N - 1);
- // Start with generating a maxplanar graph on the random tree
- // (or adding edges up to numEdges in the fashion of generating a maxplanar graph)
+ faces = (randomGraphFaceRec *)calloc((size_t)faceCapacity, sizeof(randomGraphFaceRec));
- M = numEdges <= 3 * N - 6 ? numEdges : 3 * N - 6;
+ if (!addAllPlanarEdges && numPlanarCoreEdges > N - 1)
+ optionalEdges = (randomGraphEdgeRec *)calloc((size_t)optionalEdgeCapacity, sizeof(randomGraphEdgeRec));
- // Start with the first vertex
- root = gp_LowerBoundVertices(theGraph);
+ if (faces == NULL || (!addAllPlanarEdges && numPlanarCoreEdges > N - 1 && optionalEdges == NULL))
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
+ }
- // Generally, we use v keep track of a traversal down and up all the random tree edges
- // The last variable marks the location of the last vertex that was an endpoint of the
- // most recently added edge.
- v = last = _getUnprocessedChild(theGraph, root);
+ faces[faceCount].a = lowerVertex;
+ faces[faceCount].b = lowerVertex + 1;
+ faces[faceCount].c = lowerVertex + 2;
+ faceCount++;
- // Just a safety check (all children of root are initially unprocessed)
- if (gp_IsNotVertex(theGraph, v))
- return NOTOK;
+ faces[faceCount].a = lowerVertex;
+ faces[faceCount].b = lowerVertex + 2;
+ faces[faceCount].c = lowerVertex + 1;
+ faceCount++;
- // Vertex v starts at the first unprocessed child of root and traverses around both sides
- // of the edges of the random tree until it gets back to the root... except,
- // The original version of this method generated a maxplanar graph only, but it was
- // refactored to give greater control of the number of edges. After the refactor, this
- // loop now stops when the edge count reaches M. Even for a maxplanar graph, that will
- // happen when v reaches the last unprocessed child of the last unprocessed child of root.
- // Still, we test for v != root for greater understanding of the idea of this method.
- while (v != root && gp_GetM(theGraph) < M)
- {
- // Get the next unprocessed child of v, if any. This method has the side effect
- // that it marks the edge (v, c) and hence c as being processed. This method
- // returns NIL (which not a vertex) if v has no _unprocessed_ children left.
- c = _getUnprocessedChild(theGraph, v);
-
- // If v did have an unprocessed child...
- if (gp_IsVertex(theGraph, c))
- {
- // FORWARD_LABEL_0 (see below)
- if (last != v)
- {
- if (gp_AddEdge(theGraph, last, 1, c, 1) != OK)
- return NOTOK;
- }
+ if (gp_AddEdge(theGraph, lowerVertex, 0, lowerVertex + 1, 0) != OK ||
+ gp_AddEdge(theGraph, lowerVertex + 1, 0, lowerVertex + 2, 0) != OK)
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
+ }
- // Add an edge to create a new triangular face with root, v, and the child c
- // FORWARD_LABEL_1 (see below)
- if (gp_AddEdge(theGraph, root, 1, c, 1) != OK)
- return NOTOK;
+ if (_ProcessRandomGraphOptionalEdge(theGraph, optionalEdges, optionalEdgeCapacity, &optionalEdgeCount,
+ addAllPlanarEdges, lowerVertex + 2, lowerVertex) != OK)
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
+ }
+
+ for (v = lowerVertex + 3; v < upperVertex; ++v)
+ {
+ int faceIndex = gp_GetRandomNumber(0, faceCount - 1);
+ int a = faces[faceIndex].a;
+ int b = faces[faceIndex].b;
+ int c = faces[faceIndex].c;
+ int faceVertices[3] = {a, b, c};
+ int treeEdgeIndex = gp_GetRandomNumber(0, 2);
+ int i;
- // Advance the traversal of v to the child c, and also assign c to last because
- // (root, c) is the last non-tree edge added.
- v = last = c;
+ if (gp_AddEdge(theGraph, v, 0, faceVertices[treeEdgeIndex], 0) != OK)
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
}
- // If v did not have any more unprocessed children, then we have to back up to
- // the nearest of its tree ancestors that does have an unprocessed child
- else
+ for (i = 0; i < 3; ++i)
{
- // Get the parent of v and get its next unprocessed child, if any
- p = gp_GetVertexParent(theGraph, v);
- if (gp_IsVertex(theGraph, p))
- c = _getUnprocessedChild(theGraph, p);
-
- // Loop until we find an ancestor (p) of v that does have an unprocessed child
- // This loop also creates more triangular faces as it traverses back up along
- // the child-to-parent sides of edges to the successive ancestors of v.
- // FORWARD_LABEL_2 (see below)
- while (gp_IsVertex(theGraph, p) && gp_IsNotVertex(theGraph, (c)))
+ if (i != treeEdgeIndex &&
+ _ProcessRandomGraphOptionalEdge(theGraph, optionalEdges, optionalEdgeCapacity,
+ &optionalEdgeCount, addAllPlanarEdges, v, faceVertices[i]) != OK)
{
- // Since we are in this loop, the parent p did not have
- // an unprocessed child, so we advance both p and v to
- // enable checking the next higher ancestor
- v = p;
- p = gp_GetVertexParent(theGraph, v);
-
- // Now that we have advanced upward, there is now a triangular face
- // we can create between the original v (denoted last) and the new
- // parent, which is a grandparent or higher of last.
- // This ensures that we triangulate along the path leading back
- // up to the next vertex with an unprocessed child.
- if (gp_IsVertex(theGraph, p))
- {
- // We exclude adding an edge between last and p in the special case
- // that p has ascended back up to the root because adding the edge
- // would create a duplicate of the edge added at FORWARD_LABEL_1
- if (p != root)
- {
- if (gp_AddEdge(theGraph, last, 1, p, 1) != OK)
- return NOTOK;
- }
- }
-
- // Now that we have dealt with triangulation of that path up to the new p,
- // we obtain its next unprocessed child, if any to see if we have gone
- // to a high enough ancestor that we have an unprocessed child to deal with.
- // NOTE: At the very least, there will still be an unprocessed child by the
- // time p gets to the tree root because we haven't yet reached the
- // edge limit in the outer loop condition.
- if (gp_IsVertex(theGraph, p))
- c = _getUnprocessedChild(theGraph, p);
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
}
+ }
- // Back when v != root was the outer loop condition, it was possible for v to
- // go to the root, and for p to become NIL (not a vertex). Now, that the outer
- // loop ends as soon as enough edges are added, p is always a vertex.
- // Still, we do the test here.
- if (gp_IsVertex(theGraph, p))
- {
- if (p == root)
- {
- // If p is the root, then we create a triangular face containing
- // v, p==root, and c, where v is the last vertex visited in one
- // of subtree of p==root, and c is the first vertex visited in the
- // next subtree of p== root.
- // NOTE: This is a special kind of edge called a "cross edge" that
- // joins two vertices that do not have the ancestor-descendant
- // relationship (i.e., it is not a "back edge" and so the tree
- // is not a DFS tree).
- if (gp_AddEdge(theGraph, v, 1, c, 1) != OK)
- return NOTOK;
-
- // If v advanced upward to a higher ancestor than the parent of last,
- // then we entered the loop at FORWARD_LABEL_2, which triangulated
- // on the way up, except now we must add an edge that creates a
- // triangular face with last, v, and c.
- if (v != last)
- {
- if (gp_AddEdge(theGraph, last, 1, c, 1) != OK)
- return NOTOK;
- }
-
- // NOTE: Because p is the root, we do not advance 'last' to c quite yet
- // because v will advance to c below and the next iteration of
- // the outer loop will get _its_ next unprocessed child, say c2.
- // Only once we know the identity of c2 can we add the extra edge
- // needed to create a triangular face with last, c, and c2.
- // This occurs at FORWARD_LABEL_0 above, with v=c and c=c2,
- // after which last is assigned the value c2.
- // Perhaps one day enough guilt will accrue to foster doing what
- // is needed here to allow c to be assigned to last.
- }
+ faces[faceIndex].a = a;
+ faces[faceIndex].b = b;
+ faces[faceIndex].c = v;
- // In case p is not the root, then we have already triangulated along the
- // path up from last to p, so...
- else
- {
- // We add an edge that creates a triangular face with last, p, and c.
- if (gp_AddEdge(theGraph, last, 1, c, 1) != OK)
- return NOTOK;
+ if (faceCount + 2 > faceCapacity)
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
+ }
- // And then an edge that creates a triangular face with root, last, and c.
- if (gp_AddEdge(theGraph, root, 1, c, 1) != OK)
- return NOTOK;
+ faces[faceCount].a = b;
+ faces[faceCount].b = c;
+ faces[faceCount].c = v;
+ faceCount++;
- // At which point, last can advance to c
- last = c;
- }
+ faces[faceCount].a = c;
+ faces[faceCount].b = a;
+ faces[faceCount].c = v;
+ faceCount++;
+ }
- // The main traversal tracking variable v can now advance to c
- v = c;
+ if (optionalEdges != NULL)
+ {
+ _ShuffleRandomGraphEdgeCandidates(optionalEdges, optionalEdgeCount);
+
+ for (e = 0; e < optionalEdgeCount && gp_GetM(theGraph) < numPlanarCoreEdges; ++e)
+ {
+ if (gp_AddEdge(theGraph, optionalEdges[e].u, 0, optionalEdges[e].v, 0) != OK)
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
}
}
}
- /* Add additional edges if the limit has not yet been reached. */
+ if (gp_GetM(theGraph) < numPlanarCoreEdges)
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
+ }
+
+ /* Add additional random edges if the limit has not yet been reached. */
while (gp_GetM(theGraph) < numEdges)
{
- u = _GetRandomNumber(gp_LowerBoundVertices(theGraph), gp_UpperBoundVertices(theGraph) - 1);
- v = _GetRandomNumber(gp_LowerBoundVertices(theGraph), gp_UpperBoundVertices(theGraph) - 1);
+ u = gp_GetRandomNumber(lowerVertex, upperVertex - 1);
+ v = gp_GetRandomNumber(lowerVertex, upperVertex - 1);
if (u != v && !gp_IsNeighbor(theGraph, u, v))
+ {
if (gp_AddEdge(theGraph, u, 0, v, 0) != OK)
- return NOTOK;
+ {
+ Result = NOTOK;
+ goto gp_CreateRandomGraphEx_Cleanup;
+ }
+ }
}
- /* Clear the edge types back to 'unknown' */
+gp_CreateRandomGraphEx_Cleanup:
- for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); ++e)
+ if (optionalEdges != NULL)
{
- gp_ClearEdgeType(theGraph, e);
- gp_ClearEdgeVisited(theGraph, e);
+ free(optionalEdges);
+ optionalEdges = NULL;
+ }
+ if (faces != NULL)
+ {
+ free(faces);
+ faces = NULL;
}
- /* Put all DFSParent indicators back to NIL */
-
- for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
- gp_SetVertexParent(theGraph, v, NIL);
-
- return OK;
+ return Result;
}
/********************************************************************
@@ -1940,41 +1945,26 @@ void _DetachEdgeRecord(graphP theGraph, int e)
caller can guard against these conditions by pre-testing that
u != v and that gp_FindEdge() returns NIL.
- Returns OK on success, NOTOK on failure, or AT_EDGE_CAPACITY_LIMIT if
- adding the edge would exceed the graph's edge capacity (the
- caller can use gp_DynamicAddEdge()).
+ Returns OK on success, NOTOK on failure (including when adding the
+ edge would exceed the graph's edge capacity; the caller can
+ use gp_DynamicAddEdge()).
********************************************************************/
int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink)
{
- int upos, vpos;
-
if (theGraph == NULL ||
u < gp_LowerBoundVertexStorage(theGraph) || v < gp_LowerBoundVertexStorage(theGraph) ||
u >= gp_UpperBoundVertexStorage(theGraph) || v >= gp_UpperBoundVertexStorage(theGraph))
return NOTOK;
- /* We enforce the edge limit */
-
- if (gp_GetM(theGraph) >= theGraph->edgeCapacity)
- return AT_EDGE_CAPACITY_LIMIT;
-
- if (sp_NonEmpty(theGraph->edgeHoles))
+ if ((ulink != 0 && ulink != 1) || (vlink != 0 && vlink != 1))
{
- sp_Pop(theGraph->edgeHoles, vpos);
- theGraph->numEdgeHoles = sp_GetCurrentSize(theGraph->edgeHoles);
+ return NOTOK;
}
- else
- vpos = gp_UpperBoundEdges(theGraph);
- upos = gp_GetTwin(theGraph, vpos);
-
- gp_SetNeighbor(theGraph, upos, v);
- _AttachEdgeRecord(theGraph, u, NIL, ulink, upos);
- gp_SetNeighbor(theGraph, vpos, u);
- _AttachEdgeRecord(theGraph, v, NIL, vlink, vpos);
+ if (gp_InsertEdge(theGraph, u, NIL, ulink, v, NIL, vlink) != OK)
+ return NOTOK;
- theGraph->M++;
return OK;
}
@@ -1982,46 +1972,60 @@ int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink)
gp_DynamicAddEdge()
Refer to documentation for gp_AddEdge() for parameter description.
- Calls gp_AddEdge(); if AT_EDGE_CAPACITY_LIMIT, doubles the edge
- capacity using gp_EnsureEdgeCapacity(), then retries gp_AddEdge().
+ Calls gp_DynamicInsertEdge() with insertion positions that prepend or
+ append the new edge records according to ulink and vlink.
Returns OK on success, NOTOK on failure.
********************************************************************/
int gp_DynamicAddEdge(graphP theGraph, int u, int ulink, int v, int vlink)
{
- int Result = OK;
+ if (gp_DynamicInsertEdge(theGraph, u, NIL, ulink, v, NIL, vlink) != OK)
+ return NOTOK;
- Result = gp_AddEdge(theGraph, u, ulink, v, vlink);
+ return OK;
+}
- if (Result == AT_EDGE_CAPACITY_LIMIT)
- {
- // The candidate edge capacity is double the current capacity
- int candidateEdgeCapacity = gp_GetEdgeCapacity(theGraph) << 1;
- int N = gp_GetN(theGraph);
- int newEdgeCapacity = candidateEdgeCapacity;
+/********************************************************************
+ gp_DynamicInsertEdge()
+ Refer to documentation for gp_InsertEdge() for parameter description.
- // If the candidate edge capacity exceeds the number of edges
- // needed in an undirected clique on N vertices, then attempt
- // to use that as the new edge capacity.
- if (candidateEdgeCapacity > ((N * (N - 1)) >> 1))
- newEdgeCapacity = ((N * (N - 1)) >> 1);
+ If the graph is at its edge capacity and has no reusable edge hole,
+ doubles the edge capacity using gp_EnsureEdgeCapacity() before calling
+ gp_InsertEdge().
- // However, if the edge capacity is already greater than or
- // equal to that maximum capacity needed for an undirected
- // clique on N vertices, then we allow the capacity to double
- // beyond the simple undirected graph limit.
- if (newEdgeCapacity <= gp_GetEdgeCapacity(theGraph))
- newEdgeCapacity = candidateEdgeCapacity;
+ Returns OK on success, NOTOK on failure.
+ ********************************************************************/
+int gp_DynamicInsertEdge(graphP theGraph, int u, int e_u, int e_ulink,
+ int v, int e_v, int e_vlink)
+{
+ if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ return NOTOK;
- Result = gp_EnsureEdgeCapacity(theGraph, newEdgeCapacity);
+ if (gp_GetM(theGraph) >= theGraph->edgeCapacity && sp_IsEmpty(theGraph->edgeHoles))
+ {
+ // The new edge capacity is double the current capacity. Parallel
+ // edges are supported, so the capacity is not capped at the number
+ // of edges needed for an undirected clique on N vertices.
+ long long newEdgeCapacity = ((long long)gp_GetEdgeCapacity(theGraph)) << 1;
- if (Result != OK)
- return NOTOK;
+ // If left shift would overflow signed integer, then cap it at INT_MAX.
+ if (newEdgeCapacity > INT_MAX)
+ {
+ newEdgeCapacity = INT_MAX;
+ // If unable to allocate more edges due to already being at INT_MAX,
+ // then return failure.
+ if (newEdgeCapacity <= gp_GetEdgeCapacity(theGraph))
+ return NOTOK;
+ }
- Result = gp_AddEdge(theGraph, u, ulink, v, vlink);
+ if (gp_EnsureEdgeCapacity(theGraph, (int)newEdgeCapacity) != OK)
+ return NOTOK;
}
- return Result != OK ? NOTOK : Result;
+ if (gp_InsertEdge(theGraph, u, e_u, e_ulink, v, e_v, e_vlink) != OK)
+ return NOTOK;
+
+ return OK;
}
/********************************************************************
@@ -2067,14 +2071,13 @@ int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink,
e_ulink < 0 || e_ulink > 1 || e_vlink < 0 || e_vlink > 1)
return NOTOK;
- if (gp_GetM(theGraph) >= theGraph->edgeCapacity)
- return AT_EDGE_CAPACITY_LIMIT;
-
if (sp_NonEmpty(theGraph->edgeHoles))
{
sp_Pop(theGraph->edgeHoles, vpos);
theGraph->numEdgeHoles = sp_GetCurrentSize(theGraph->edgeHoles);
}
+ else if (gp_GetM(theGraph) >= theGraph->edgeCapacity)
+ return AT_EDGE_CAPACITY_LIMIT;
else
vpos = gp_UpperBoundEdges(theGraph);
@@ -2106,10 +2109,7 @@ int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink,
NOTE: This method reinitializes the edge records for e and its twin
in the base graph data structure. Extensions having parallel
- edge record extension data elements must implement and use their
- own edge deletion methods, which must then call gp_DeleteEdge().
- Calling gp_DeleteEdge() does not currently clear data in extension
- data structures.
+ edge record extension data elements must overload gp_DeleteEdge().
Returns OK on success, NOTOK on failure
****************************************************************************/
@@ -2122,6 +2122,11 @@ int gp_DeleteEdge(graphP theGraph, int e)
gp_EdgeNotInUse(theGraph, e))
return NOTOK;
+ return theGraph->functions->fpDeleteEdge(theGraph, e);
+}
+
+int _DeleteEdge(graphP theGraph, int e)
+{
// Delete the edge records e and eTwin from their adjacency lists.
_DetachEdgeRecord(theGraph, e);
_DetachEdgeRecord(theGraph, gp_GetTwin(theGraph, e));
@@ -2139,7 +2144,7 @@ int gp_DeleteEdge(graphP theGraph, int e)
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. */
+ // then record a new hole in the edge array.
if (e < gp_UpperBoundEdges(theGraph))
{
if (theGraph->edgeHoles->size + 1 >= theGraph->edgeHoles->capacity)
@@ -2153,6 +2158,56 @@ int gp_DeleteEdge(graphP theGraph, int e)
return OK;
}
+int gp_ClearEdgeDirectionFlags(graphP theGraph)
+{
+ if (theGraph == NULL)
+ return NOTOK;
+
+ for (int e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); e += 2)
+ {
+ if (gp_EdgeInUse(theGraph, e))
+ {
+ // Clear direction flags if non-loop edge
+ if (gp_GetNeighbor(theGraph, gp_GetTwin(theGraph, e)) != gp_GetNeighbor(theGraph, e))
+ gp_SetDirection(theGraph, e, 0);
+ }
+ }
+
+ theGraph->graphFlags &= ~GRAPHFLAGS_DIRECTEDEDGEDETECTED;
+ return OK;
+}
+
+/*
+ * Reverse every in-use directed edge. This can be used along with a directed
+ * depth-first search when computing strongly connected components.
+ */
+int gp_TransposeDirectedGraph(graphP theGraph)
+{
+ if (theGraph == NULL)
+ return NOTOK;
+
+ for (int e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); e += 2)
+ {
+ if (gp_EdgeInUse(theGraph, e))
+ {
+ int direction = gp_GetDirection(theGraph, e);
+
+ if (direction == EDGEFLAG_DIRECTION_INONLY ||
+ direction == EDGEFLAG_DIRECTION_OUTONLY)
+ {
+ int transposedDirection = direction == EDGEFLAG_DIRECTION_INONLY
+ ? EDGEFLAG_DIRECTION_OUTONLY
+ : EDGEFLAG_DIRECTION_INONLY;
+
+ gp_SetDirection(theGraph, e, 0);
+ gp_SetDirection(theGraph, e, transposedDirection);
+ }
+ }
+ }
+
+ return OK;
+}
+
/********************************************************************
_RestoreEdgeRecord()
@@ -2361,12 +2416,24 @@ int _HideVertex(graphP theGraph, int vertex)
// Cycle through all the edges, pushing and hiding each
while (gp_IsEdge(theGraph, e))
{
+ if (sp_GetCurrentSize(theGraph->theStack) >= sp_GetCapacity(theGraph->theStack))
+ {
+ gp_ErrorMessage("_HideVertex() is attempting to push to a full stack.");
+ return NOTOK;
+ }
+
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
e = gp_GetNextEdge(theGraph, e);
}
// Push the additional integers needed by gp_RestoreVertex()
+ if (sp_GetCurrentSize(theGraph->theStack) + 7 > sp_GetCapacity(theGraph->theStack))
+ {
+ gp_ErrorMessage("_HideVertex() is attempting to push to a full stack.");
+ return NOTOK;
+ }
+
sp_Push(theGraph->theStack, hiddenEdgeStackBottom);
sp_Push(theGraph->theStack, NIL);
sp_Push(theGraph->theStack, NIL);
@@ -2411,6 +2478,13 @@ int _ContractEdge(graphP theGraph, int e)
v = gp_GetNeighbor(theGraph, e);
eBefore = gp_GetNextEdge(theGraph, e);
+
+ if (sp_GetCurrentSize(theGraph->theStack) >= sp_GetCapacity(theGraph->theStack))
+ {
+ gp_ErrorMessage("_ContractEdge() is attempting to push to a full stack.");
+ return NOTOK;
+ }
+
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
@@ -2477,6 +2551,11 @@ int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore)
if (gp_IsEdge(theGraph, e))
{
int result = gp_ContractEdge(theGraph, e);
+ int hiddenEdgesStackBottomIndex;
+ int hiddenEdgesStackBottomValue;
+
+ if (result != OK)
+ return result;
// The edge contraction operation pushes one hidden edge then
// recursively calls this method. This method then pushes K
@@ -2489,8 +2568,8 @@ int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore)
// 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);
+ hiddenEdgesStackBottomIndex = sp_GetCurrentSize(theGraph->theStack) - 7;
+ hiddenEdgesStackBottomValue = sp_Get(theGraph->theStack, hiddenEdgesStackBottomIndex);
sp_Set(theGraph->theStack, hiddenEdgesStackBottomIndex, hiddenEdgesStackBottomValue - 1);
@@ -2520,6 +2599,18 @@ int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore)
{
if (gp_GetVisited(theGraph, gp_GetNeighbor(theGraph, e)))
{
+ if (sp_GetCurrentSize(theGraph->theStack) >= sp_GetCapacity(theGraph->theStack))
+ {
+ gp_ErrorMessage("_IdentifyVertices() is attempting to push to a full stack.");
+ e = gp_GetFirstEdge(theGraph, u);
+ while (gp_IsEdge(theGraph, e))
+ {
+ gp_ClearVisited(theGraph, gp_GetNeighbor(theGraph, e));
+ e = gp_GetNextEdge(theGraph, e);
+ }
+ return NOTOK;
+ }
+
sp_Push(theGraph->theStack, e);
gp_HideEdge(theGraph, e);
}
@@ -2536,6 +2627,12 @@ int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore)
// Push the hiddenEdgeStackBottom as a record of how many hidden
// edges were pushed (also, see above for Contract Edge adjustment)
+ if (sp_GetCurrentSize(theGraph->theStack) + 7 > sp_GetCapacity(theGraph->theStack))
+ {
+ gp_ErrorMessage("_IdentifyVertices() is attempting to push to a full stack.");
+ return NOTOK;
+ }
+
sp_Push(theGraph->theStack, hiddenEdgeStackBottom);
// Moving v's adjacency list to u is aided by knowing the predecessor
@@ -2655,7 +2752,10 @@ 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)
+ {
+ gp_ErrorMessage("_RestoreVertex() is attempting to pop from an empty stack.");
return NOTOK;
+ }
sp_Pop(theGraph->theStack, v);
sp_Pop(theGraph->theStack, u);
@@ -2699,7 +2799,7 @@ int _RestoreVertex(graphP theGraph)
if (gp_IsEdge(theGraph, e_v_first))
gp_SetPrevEdge(theGraph, e_v_first, NIL);
if (gp_IsEdge(theGraph, e_v_last))
- gp_SetPrevEdge(theGraph, e_v_last, NIL);
+ gp_SetNextEdge(theGraph, e_v_last, NIL);
// For each edge record restored to v's adjacency list, reassign the 'v' member
// of each twin edge record to indicate v rather than u.
@@ -2712,6 +2812,12 @@ int _RestoreVertex(graphP theGraph)
}
// Restore the hidden edges of v, if any
+ if (sp_IsEmpty(theGraph->theStack))
+ {
+ gp_ErrorMessage("_RestoreVertex() is attempting to pop from an empty stack.");
+ return NOTOK;
+ }
+
sp_Pop(theGraph->theStack, HESB);
return _RestoreHiddenEdges(theGraph, HESB);
}
@@ -2762,8 +2868,8 @@ int gp_RestoreVertices(graphP theGraph)
int _ComputeEdgeRecordType(graphP theGraph, int a, int b, int edgeType)
{
- a = gp_IsVirtualVertex(theGraph, a) ? gp_GetVertexFromBicompRoot(theGraph, a) : a;
- b = gp_IsVirtualVertex(theGraph, b) ? gp_GetVertexFromBicompRoot(theGraph, b) : b;
+ a = gp_IsVirtualVertex(theGraph, a) ? _gp_GetVertexFromBicompRoot(theGraph, a) : a;
+ b = gp_IsVirtualVertex(theGraph, b) ? _gp_GetVertexFromBicompRoot(theGraph, b) : b;
if (a < b)
return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_CHILD : EDGE_TYPE_FORWARD;
@@ -2786,8 +2892,8 @@ int _RestoreEdgeType(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_GetVertexFromBicompRoot(theGraph, u)) : u;
- v_orig = gp_IsVirtualVertex(theGraph, v) ? (gp_GetVertexFromBicompRoot(theGraph, v)) : v;
+ u_orig = gp_IsVirtualVertex(theGraph, u) ? (_gp_GetVertexFromBicompRoot(theGraph, u)) : u;
+ v_orig = gp_IsVirtualVertex(theGraph, v) ? (_gp_GetVertexFromBicompRoot(theGraph, v)) : v;
// Get the edge for which we will set the type
diff --git a/planarity/c/graphLib/graph.h b/planarity/c/graphLib/graph.h
index 62f0db0..b0d55a3 100644
--- a/planarity/c/graphLib/graph.h
+++ b/planarity/c/graphLib/graph.h
@@ -67,7 +67,11 @@ extern "C"
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);
+ int gp_DynamicInsertEdge(graphP theGraph, int u, int e_u, int e_ulink,
+ int v, int e_v, int e_vlink);
int gp_DeleteEdge(graphP theGraph, int e);
+ int gp_ClearEdgeDirectionFlags(graphP theGraph);
+ int gp_TransposeDirectedGraph(graphP theGraph);
// Intermediate graph structure manipulators
void gp_HideEdge(graphP theGraph, int e);
@@ -89,6 +93,7 @@ extern "C"
Bits 16-23 reserved for Planarity-Related
bits 24-31 reserved for future expansion
*/
+#define GRAPHFLAGS_DIRECTEDEDGEDETECTED 1
#define gp_GetGraphFlags(theGraph) ((theGraph)->graphFlags)
// For graph embedding methods and declarations, see graphPlanarity.h
@@ -293,7 +298,7 @@ extern "C"
#define gp_SetAdjacentEdge(theGraph, e, theLink, newEdge) (theGraph->E[e].link[theLink] = newEdge)
////////////////////////////////////////////
-// gp_IsEdge() helps detect the end of an
+// gp_IsEdge() helps detect the end of an
// adjacency list iteration loop
////////////////////////////////////////////
#ifdef USE_1BASEDARRAYS
@@ -325,25 +330,37 @@ extern "C"
#define gp_ClearEdgeMarked(theGraph, e) (theGraph->E[e].flags &= ~EDGE_MARKED_MASK)
#define gp_SetEdgeMarked(theGraph, e) (theGraph->E[e].flags |= EDGE_MARKED_MASK)
-// The edge type is defined by bits 2-4, 4+8+16=28
-#define EDGE_TYPE_MASK 28
-
-// Call gp_GetEdgeType(), then compare to one of these four possibilities
-// EDGE_TYPE_CHILD - edge record points to a neighboring DFS child
-// EDGE_TYPE_FORWARD - edge record points to a DFS descendant, not a DFS child
-// EDGE_TYPE_PARENT - edge record points to the DFS parent
-// EDGE_TYPE_BACK - edge record points to a DFS ancestor, not the DFS parent
-// NOTE: A parent/child tree edge has bit 3 (8) set, forward/back edges do not
-#define EDGE_TYPE_CHILD 28
-#define EDGE_TYPE_FORWARD 20
-#define EDGE_TYPE_PARENT 12
-#define EDGE_TYPE_BACK 4
+// The edge type is defined by bits 2-4 and 8, 4+8+16+256=284
+// Bit 2 set means the edge record neighbor field indicates a parent (in a
+// tree edge) or ancestor (in a "back" edge, aka a cotree edge)
+// Bit 3 set means edge record is part of a "tree" edge whose endpoints share
+// the direct DFS parent/child relationship
+// Bit 4 set means the edge record's neighbor field indicates either a child
+// (in a tree edge) or a descendant (in a "back" edge, aka "cotree" edge)
+// Bit 5 set means the edge record is in a "cross" edge that is neither a
+// tree edge nor a back edge (so, bits 2, 3, and 4 are clear).
+#define EDGE_TYPE_MASK 284
// EDGE_TYPE_NOTDEFINED - the edge record type has not been defined
-// EDGE_TYPE_TREE - edge record is part of a randomly generated tree
-// NOTE: EDGE_TYPE_TREE uses the same bit 3 as DFS parent and child edges above
#define EDGE_TYPE_NOTDEFINED 0
+
+// EDGE_TYPE_TREE - gives a name to the bit indicating a tree edge
+// EDGE_TYPE_PARENT - edge record neighbor field indicates DFS parent
+// EDGE_TYPE_CHILD - edge record neighbor field indicates a DFS child
#define EDGE_TYPE_TREE 8
+#define EDGE_TYPE_PARENT 12
+#define EDGE_TYPE_CHILD 28
+
+// EDGE_TYPE_BACK - edge record points to a DFS ancestor, not the DFS parent
+// EDGE_TYPE_FORWARD - edge record points to a DFS descendant, not a DFS child
+#define EDGE_TYPE_BACK 4
+#define EDGE_TYPE_FORWARD 20
+
+// EDGE_TYPE_CROSS - edge record and its twin represent a cross edge
+// Bit3 is not set because the edge is not a tree edge
+// Bits 2 and 4 are not set because the edge's endpoings are not
+// in a parent-or-ancestor/child-or-descendant relationship
+#define EDGE_TYPE_CROSS 256
#define gp_GetEdgeType(theGraph, e) (theGraph->E[e].flags & EDGE_TYPE_MASK)
#define gp_ClearEdgeType(theGraph, e) (theGraph->E[e].flags &= ~EDGE_TYPE_MASK)
@@ -361,28 +378,39 @@ extern "C"
#define EDGEFLAG_DIRECTION_OUTONLY 128
#define EDGEFLAG_DIRECTION_MASK 192
+// NOTE: Edge 'flags' bit 8 used by EDGE_TYPE_CROSS above,
+// so next available bit is bit 9 = 512
+
// 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 direction and e's twin edge record is set to the opposing setting.
-#define gp_SetDirection(theGraph, e, direction) \
- { \
- if (direction == EDGEFLAG_DIRECTION_INONLY) \
- { \
- theGraph->E[e].flags |= EDGEFLAG_DIRECTION_INONLY; \
- theGraph->E[gp_GetTwin(theGraph, e)].flags |= EDGEFLAG_DIRECTION_OUTONLY; \
- } \
- else if (direction == EDGEFLAG_DIRECTION_OUTONLY) \
- { \
- theGraph->E[e].flags |= EDGEFLAG_DIRECTION_OUTONLY; \
- theGraph->E[gp_GetTwin(theGraph, e)].flags |= EDGEFLAG_DIRECTION_INONLY; \
- } \
- else \
- { \
- theGraph->E[e].flags &= ~EDGEFLAG_DIRECTION_MASK; \
- theGraph->E[gp_GetTwin(theGraph, e)].flags &= ~EDGEFLAG_DIRECTION_MASK; \
- } \
+#define gp_SetDirection(theGraph, e, direction) \
+ { \
+ if (direction == EDGEFLAG_DIRECTION_INONLY) \
+ { \
+ theGraph->E[e].flags |= EDGEFLAG_DIRECTION_INONLY; \
+ theGraph->E[gp_GetTwin(theGraph, e)].flags |= EDGEFLAG_DIRECTION_OUTONLY; \
+ if (gp_GetNeighbor(theGraph, e) != gp_GetNeighbor(theGraph, gp_GetTwin(theGraph, e))) \
+ { \
+ theGraph->graphFlags |= GRAPHFLAGS_DIRECTEDEDGEDETECTED; \
+ } \
+ } \
+ else if (direction == EDGEFLAG_DIRECTION_OUTONLY) \
+ { \
+ theGraph->E[e].flags |= EDGEFLAG_DIRECTION_OUTONLY; \
+ theGraph->E[gp_GetTwin(theGraph, e)].flags |= EDGEFLAG_DIRECTION_INONLY; \
+ if (gp_GetNeighbor(theGraph, e) != gp_GetNeighbor(theGraph, gp_GetTwin(theGraph, e))) \
+ { \
+ theGraph->graphFlags |= GRAPHFLAGS_DIRECTEDEDGEDETECTED; \
+ } \
+ } \
+ else \
+ { \
+ theGraph->E[e].flags &= ~EDGEFLAG_DIRECTION_MASK; \
+ theGraph->E[gp_GetTwin(theGraph, e)].flags &= ~EDGEFLAG_DIRECTION_MASK; \
+ } \
}
// Iterate through all edges with gp_LowerBoundEdges, gp_UpperBoundEdges, and gp_EdgeInUse
@@ -415,7 +443,7 @@ extern "C"
// This value is returned by gp_AddEdge() and gp_InsertEdge() if adding or inserting
// the edge would exceed the edge capacity limit. The limit can be increased by
-// calling gp_EnsureEdgeCapacity(), or by calling gp_DynamicAddEdge().
+// calling gp_EnsureEdgeCapacity(), gp_DynamicAddEdge(), or gp_DynamicInsertEdge().
#define AT_EDGE_CAPACITY_LIMIT -1
// A bounds-checking version of gp_IsEdge() for DEBUG mode compilation
@@ -434,7 +462,7 @@ extern "C"
typedef struct stackStruct stackStruct;
typedef stackStruct *stackP;
- // Declaration of package private data types for extending the base Graph class
+ // Declaration of package private data types for extending the base Graph class
// with subclasses having data members and function overloads
typedef struct graphExtensionStruct graphExtensionStruct;
typedef graphExtensionStruct *graphExtensionP;
@@ -463,9 +491,9 @@ extern "C"
extensions: an object-oriented hierarchy of graph classes is implemented
manually as a list of extensions for data of any subclasses
with which a graph has been extended.
- extensionLookupTable: if not NULL, keeps an array of pointers to the
+ extensionLookupTable: if not NULL, keeps an array of pointers to the
extensions indexed by ID for constant-time lookup.
- functions: object-oriented class hierarchies includes virtual function
+ functions: object-oriented class hierarchies includes virtual function
overloading, which is provided by this function pointer table.
privateData: pointer to a package private data structure that can be
@@ -487,7 +515,7 @@ extern "C"
// Used by base Graph class and its subclasses
stackP theStack;
- // Provides ability to subclass the base Graph,
+ // Provides ability to subclass the base Graph,
// including virtual function overloads by subclasses
graphExtensionP extensions;
graphExtensionP *extensionLookupTable;
diff --git a/planarity/c/graphLib/graph.private.h b/planarity/c/graphLib/graph.private.h
index 783109a..7e5f68c 100644
--- a/planarity/c/graphLib/graph.private.h
+++ b/planarity/c/graphLib/graph.private.h
@@ -4,8 +4,8 @@ All rights reserved.
See the LICENSE.TXT file for licensing information.
*/
-#ifndef GRAPHUTILS_PRIVATE_H
-#define GRAPHUTILS_PRIVATE_H
+#ifndef GRAPH_PRIVATE_H
+#define GRAPH_PRIVATE_H
#include "extensionSystem/graphExtensions.h"
#include "extensionSystem/graphExtensions.private.h"
@@ -206,4 +206,7 @@ extern "C"
}
#endif
-#endif /* GRAPHUTILS_PRIVATE_H */
+// This number can just be made bigger if ever needed
+#define MAXNUMSUPPORTEDEXTENSIONS 32
+
+#endif /* GRAPH_PRIVATE_H */
diff --git a/planarity/c/graphLib/graphDFSUtils.c b/planarity/c/graphLib/graphDFSUtils.c
index ab64376..6b8d574 100644
--- a/planarity/c/graphLib/graphDFSUtils.c
+++ b/planarity/c/graphLib/graphDFSUtils.c
@@ -16,10 +16,12 @@ See the LICENSE.TXT file for licensing information.
#include "planarityRelated/graphPlanarity.private.h"
// Private methods, except exported within library
+int _DepthFirstSearchDirected(graphP theGraph);
int _SortVertices(graphP theGraph);
// Imported methods
-extern void _ClearVertexVisitedFlags(graphP theGraph, int);
+extern void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices);
+extern int _FillVertexVisitedIndexes(graphP theGraph, int FillValue);
/********************************************************************
gp_ExtendWith_DFSUtils()
@@ -40,7 +42,7 @@ extern void _ClearVertexVisitedFlags(graphP theGraph, int);
int gp_ExtendWith_DFSUtils(graphP theGraph)
{
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// if the Graph has already been extended with DFS Utils,
@@ -132,7 +134,7 @@ int gp_DepthFirstSearch(graphP theGraph)
for (DFI = v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
{
- if (gp_IsNotDFSTreeRoot(theGraph, v))
+ if (_gp_IsNotDFSTreeRoot(theGraph, v))
continue;
sp_Push2(theStack, NIL, NIL);
@@ -179,6 +181,189 @@ int gp_DepthFirstSearch(graphP theGraph)
theGraph->graphFlags |= GRAPHFLAGS_DFSNUMBERED;
+ if (gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DFSNUMBERED_DIRECTED)
+ {
+ theGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED_DIRECTED;
+ if (_FillVertexVisitedIndexes(theGraph, 0) != OK)
+ return NOTOK;
+ }
+
+ return OK;
+}
+
+/********************************************************************
+ gp_DepthFirstSearchEx()
+
+ Routes to the requested depth-first search variant.
+ ********************************************************************/
+
+int gp_DepthFirstSearchEx(graphP theGraph, unsigned mode)
+{
+ if (theGraph == NULL)
+ return NOTOK;
+
+ if (mode == DFSMODE_UNDIRECTED)
+ {
+ if (gp_DepthFirstSearch(theGraph) == OK)
+ return OK;
+ else
+ return NOTOK;
+ }
+ else if (mode == DFSMODE_DIRECTED)
+ {
+ if (_DepthFirstSearchDirected(theGraph) == OK)
+ return OK;
+ else
+ return NOTOK;
+ }
+
+ return NOTOK;
+}
+
+/********************************************************************
+ _DepthFirstSearchDirected()
+
+ Performs a directed depth-first search. Discovery times are stored
+ in vertex index fields and finish times are stored in visitedIndex.
+ Only outgoing and undirected edge records are traversed.
+ ********************************************************************/
+
+int _DepthFirstSearchDirected(graphP theGraph)
+{
+ stackP theStack;
+ int timer = 1;
+ int v, u, w, e, stackItem, numEdgesPushed;
+
+ if (theGraph == NULL)
+ return NOTOK;
+
+ if (gp_ExtendWith_DFSUtils(theGraph) != OK)
+ return NOTOK;
+
+ theStack = theGraph->theStack;
+
+ // One integer is pushed per eligible edge record, plus at most one
+ // finish event per active DFS tree vertex. The graph stack capacity
+ // required by the undirected DFS is sufficient for these events.
+ if (sp_GetCapacity(theStack) < 2 * 2 * gp_GetM(theGraph) + 2)
+ return NOTOK;
+
+ sp_ClearStack(theStack);
+
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ {
+ gp_SetIndex(theGraph, v, 0);
+ gp_SetVertexVisitedIndex(theGraph, v, 0);
+ gp_SetVertexParent(theGraph, v, NIL);
+ }
+
+ for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); ++e)
+ {
+ if (gp_EdgeInUse(theGraph, e))
+ {
+ gp_ClearEdgeType(theGraph, e);
+ gp_ClearEdgeMarked(theGraph, e);
+ }
+ }
+
+ // Process each directed DFS tree, including isolated vertices.
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ {
+ if (gp_GetIndex(theGraph, v) != 0)
+ continue;
+
+ gp_SetIndex(theGraph, v, timer++);
+ gp_SetVertexParent(theGraph, v, NIL);
+
+ numEdgesPushed = 0;
+ // Adjacencies are stored in the reverse of their file order, so
+ // walking first-to-last pushes the displayed list right-to-left.
+ e = gp_GetFirstEdge(theGraph, v);
+ while (gp_IsEdge(theGraph, e))
+ {
+ if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY)
+ {
+ // This first-pushed edge is the last edge that will be
+ // processed for v because the stack reverses the order.
+ if (numEdgesPushed == 0)
+ gp_SetEdgeMarked(theGraph, e);
+ sp_Push(theStack, e);
+ ++numEdgesPushed;
+ }
+ e = gp_GetNextEdge(theGraph, e);
+ }
+
+ if (numEdgesPushed == 0)
+ gp_SetVertexVisitedIndex(theGraph, v, timer++);
+
+ while (sp_NonEmpty(theStack))
+ {
+ sp_Pop(theStack, stackItem);
+
+ // A negative stack item is a deferred finish event. Deferring
+ // this event is necessary when a vertex's marked final edge is
+ // a tree edge: the new child's subtree must finish first.
+ if (stackItem < gp_LowerBoundEdges(theGraph))
+ {
+ u = -stackItem - 1;
+ gp_SetVertexVisitedIndex(theGraph, u, timer++);
+ continue;
+ }
+
+ e = stackItem;
+ u = gp_GetNeighbor(theGraph, gp_GetTwin(theGraph, e));
+ w = gp_GetNeighbor(theGraph, e);
+
+ if (gp_GetIndex(theGraph, w) == 0)
+ {
+ gp_ResetEdgeType(theGraph, e, EDGE_TYPE_TREE);
+ gp_SetIndex(theGraph, w, timer++);
+ gp_SetVertexParent(theGraph, w, u);
+
+ if (gp_GetEdgeMarked(theGraph, e))
+ {
+ gp_ClearEdgeMarked(theGraph, e);
+ sp_Push(theStack, -u - 1);
+ }
+
+ numEdgesPushed = 0;
+ e = gp_GetFirstEdge(theGraph, w);
+ while (gp_IsEdge(theGraph, e))
+ {
+ if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY)
+ {
+ if (numEdgesPushed == 0)
+ gp_SetEdgeMarked(theGraph, e);
+ sp_Push(theStack, e);
+ ++numEdgesPushed;
+ }
+ e = gp_GetNextEdge(theGraph, e);
+ }
+
+ if (numEdgesPushed == 0)
+ gp_SetVertexVisitedIndex(theGraph, w, timer++);
+ }
+ else
+ {
+ if (gp_GetVertexVisitedIndex(theGraph, w) == 0)
+ gp_ResetEdgeType(theGraph, e, EDGE_TYPE_BACK);
+ else if (gp_GetIndex(theGraph, w) < gp_GetIndex(theGraph, u))
+ gp_ResetEdgeType(theGraph, e, EDGE_TYPE_CROSS);
+ else
+ gp_ResetEdgeType(theGraph, e, EDGE_TYPE_FORWARD);
+
+ if (gp_GetEdgeMarked(theGraph, e))
+ {
+ gp_ClearEdgeMarked(theGraph, e);
+ gp_SetVertexVisitedIndex(theGraph, u, timer++);
+ }
+ }
+ }
+ }
+
+ theGraph->graphFlags |= GRAPHFLAGS_DFSNUMBERED_DIRECTED;
+ theGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED;
+
return OK;
}
@@ -280,7 +465,7 @@ int _SortVertices(graphP theGraph)
/* Convert DFSParent from v to DFI(v) or vice versa */
for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
- if (gp_IsNotDFSTreeRoot(theGraph, v))
+ if (_gp_IsNotDFSTreeRoot(theGraph, v))
gp_SetVertexParent(theGraph, v, gp_GetIndex(theGraph, gp_GetVertexParent(theGraph, v)));
/* Sort by 'v using constant time random access. Move each vertex to its
@@ -320,6 +505,7 @@ int _SortVertices(graphP theGraph)
/* Invert the bit that records the sort order of the graph */
+ theGraph->graphFlags &= ~GRAPHFLAGS_LOWPOINTSCOMPUTED;
theGraph->graphFlags ^= GRAPHFLAGS_SORTEDBYDFI;
_gp_LogLine("graphDFSUtils.c/_SortVertices() end\n");
@@ -364,6 +550,12 @@ int gp_ComputeLowpoints(graphP theGraph)
if (theGraph == NULL)
return NOTOK;
+ if (gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DIRECTEDEDGEDETECTED)
+ {
+ gp_ErrorMessage("gp_ComputeLowpoints() does not support directed graphs.");
+ return NOTOK;
+ }
+
if (gp_ExtendWith_DFSUtils(theGraph) != OK)
return NOTOK;
@@ -458,6 +650,8 @@ int gp_ComputeLowpoints(graphP theGraph)
_gp_LogLine("graphDFSUtils.c/gp_ComputeLowpoints() end\n");
+ theGraph->graphFlags |= GRAPHFLAGS_LOWPOINTSCOMPUTED;
+
return OK;
}
@@ -483,6 +677,12 @@ int gp_ComputeLeastAncestors(graphP theGraph)
if (theGraph == NULL)
return NOTOK;
+ if (gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DIRECTEDEDGEDETECTED)
+ {
+ gp_ErrorMessage("gp_ComputeLeastAncestors() does not support directed graphs.");
+ return NOTOK;
+ }
+
if (gp_ExtendWith_DFSUtils(theGraph) != OK)
return NOTOK;
@@ -550,6 +750,31 @@ int gp_ComputeLeastAncestors(graphP theGraph)
return OK;
}
+/********************************************************************
+ gp_CountConnectedComponents()
+
+ Once the DFS tree has been created in theGraph, this method returns
+ the number of connected components identified by DFS tree roots.
+ Returns -1 on error, such as a NULL graph or DFS tree not created yet.
+ ********************************************************************/
+int gp_CountConnectedComponents(graphP theGraph)
+{
+ int v, connectedComponents;
+
+ if (theGraph == NULL)
+ return -1;
+
+ if (!(gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DFSNUMBERED))
+ return -1;
+
+ connectedComponents = 0;
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ if (_gp_IsDFSTreeRoot(theGraph, v))
+ connectedComponents++;
+
+ return connectedComponents;
+}
+
/********************************************************************
gp_GetParent()
@@ -573,6 +798,27 @@ int gp_GetParent(graphP theGraph, int v)
return gp_GetVertexParent(theGraph, v);
}
+/********************************************************************
+ gp_GetVisitedIndex()
+
+ This method returns the visited index of the given vertex v.
+ Returns NIL on error, such as invalid parameters.
+ ********************************************************************/
+int gp_GetVisitedIndex(graphP theGraph, int v)
+{
+ if (theGraph == NULL ||
+ v < gp_LowerBoundVertices(theGraph) || v >= gp_UpperBoundVertices(theGraph))
+ {
+#ifdef DEBUG
+ NOTOK;
+ ;
+#endif
+ return NIL;
+ }
+
+ return gp_GetVertexVisitedIndex(theGraph, v);
+}
+
/********************************************************************
gp_GetLeastAncestor()
diff --git a/planarity/c/graphLib/graphDFSUtils.h b/planarity/c/graphLib/graphDFSUtils.h
index 71a3216..b1014f7 100644
--- a/planarity/c/graphLib/graphDFSUtils.h
+++ b/planarity/c/graphLib/graphDFSUtils.h
@@ -30,14 +30,25 @@ extern "C"
GRAPHFLAGS_SORTEDBYDFI records whether the graph is in original vertex order
or sorted by depth first index. Successive calls to the
gp_SortVertices() utility method below toggle this bit.
+ GRAPHFLAGS_LOWPOINTSCOMPUTED records whether lowpoint calculations have
+ been performed on the graph.
+ GRAPHFLAGS_DFSNUMBERED_DIRECTED is set when a directed depth-first search
+ performs the DFS numbering.
*/
#define GRAPHFLAGS_EXTENDEDWITH_DFSUTILS 256
#define GRAPHFLAGS_DFSNUMBERED 512
#define GRAPHFLAGS_SORTEDBYDFI 1024
+#define GRAPHFLAGS_LOWPOINTSCOMPUTED 2048
+#define GRAPHFLAGS_DFSNUMBERED_DIRECTED 4096
// DFS-related utility methods that create a DFS tree, sort vertices and
// compute least ancestor and lowpoint values
int gp_DepthFirstSearch(graphP theGraph);
+ int gp_DepthFirstSearchEx(graphP theGraph, unsigned mode);
+
+#define DFSMODE_UNDIRECTED 1
+#define DFSMODE_DIRECTED 2
+
int gp_SortVertices(graphP theGraph);
int gp_ComputeLowpoints(graphP theGraph);
int gp_ComputeLeastAncestors(graphP theGraph);
@@ -45,14 +56,16 @@ extern "C"
// Additional DFS-related uitility methods (functions and macros) that assume
// one or more of the above methods have been called to create a DFS tree,
// sort vertices and/or compute least ancestor and lowpoint values
+ int gp_CountConnectedComponents(graphP theGraph);
int gp_GetParent(graphP theGraph, int v);
+ int gp_GetVisitedIndex(graphP theGraph, int v);
int gp_GetLeastAncestor(graphP theGraph, int v);
int gp_GetLowpoint(graphP theGraph, int v);
// A DFS tree root is one that has no DFS parent. There is one DFS tree root
// per connected component of a graph (connected, not biconnected; component, not bicomp)
-#define gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(theGraph, gp_GetVertexParent(theGraph, v))
-#define gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(theGraph, gp_GetVertexParent(theGraph, v))
+#define gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(theGraph, gp_GetParent(theGraph, v))
+#define gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(theGraph, gp_GetParent(theGraph, v))
// Mapping between bicomp roots and virtual vertex locations used to store them.
// A cut vertex v separates one or more of its DFS children, say c1 and c2, from
@@ -70,7 +83,7 @@ extern "C"
// pairs of vertices in, respectively, T(c1) and T(c2).
#define gp_GetBicompRootFromDFSChild(theGraph, c) ((c) + gp_GetN(theGraph))
#define gp_GetDFSChildFromBicompRoot(theGraph, R) ((R) - gp_GetN(theGraph))
-#define gp_GetVertexFromBicompRoot(theGraph, R) gp_GetVertexParent(theGraph, gp_GetDFSChildFromBicompRoot(theGraph, R))
+#define gp_GetVertexFromBicompRoot(theGraph, R) gp_GetParent(theGraph, gp_GetDFSChildFromBicompRoot(theGraph, R))
#define gp_IsBicompRoot(theGraph, v) ((v) >= gp_LowerBoundVirtualVertices(theGraph))
// If a vertex v is a cut vertex that separates one of its DFS children, say c,
diff --git a/planarity/c/graphLib/graphDFSUtils.private.h b/planarity/c/graphLib/graphDFSUtils.private.h
index 75e956c..c2059ce 100644
--- a/planarity/c/graphLib/graphDFSUtils.private.h
+++ b/planarity/c/graphLib/graphDFSUtils.private.h
@@ -8,6 +8,7 @@ See the LICENSE.TXT file for licensing information.
#define GRAPHDFSUTILS_PRIVATE_H
#include "graph.private.h"
+#include "graphDFSUtils.h"
#ifdef __cplusplus
extern "C"
@@ -23,41 +24,40 @@ extern "C"
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))
+ visitedIndex: enables algorithms to manage vertex visitation with more than
+ just a flag. In a directed depth-first search, the vertex index
+ indicates the discovery time, so visitedIndex is needed for the
+ finish time. The planarity test uses this member to flag
+ visitation as a step number so that it implicitly resets on each
+ vertex step of embedding. The planar graph drawing method
+ signifies a first visitation by storing the index of the first
+ _edge_ used to reach a vertex.
*/
struct DFSUtils_VertexInfo
{
- int parent, leastAncestor, lowpoint;
+ int parent, leastAncestor, lowpoint, visitedIndex;
};
typedef struct DFSUtils_VertexInfo DFSUtils_VertexInfo;
typedef DFSUtils_VertexInfo *DFSUtils_VertexInfoP;
- struct vertexInfoRec
- {
- int parent, leastAncestor, lowpoint;
-
- int visitedInfo;
-
- int pertinentEdge,
- pertinentRoots,
- futurePertinentChild,
- sortedDFSChildList,
- fwdEdgeList;
- };
-
- typedef struct vertexInfoRec vertexInfoRec;
- typedef vertexInfoRec *vertexInfoP;
-
#define gp_GetVertexParent(theGraph, v) (theGraphDVI(theGraph)[v].parent)
#define gp_SetVertexParent(theGraph, v, theParent) (theGraphDVI(theGraph)[v].parent = theParent)
+#define _gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(theGraph, gp_GetVertexParent(theGraph, v))
+#define _gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(theGraph, gp_GetVertexParent(theGraph, v))
+#define _gp_GetVertexFromBicompRoot(theGraph, R) gp_GetVertexParent(theGraph, gp_GetDFSChildFromBicompRoot(theGraph, R))
+
#define gp_GetVertexLeastAncestor(theGraph, v) (theGraphDVI(theGraph)[v].leastAncestor)
#define gp_SetVertexLeastAncestor(theGraph, v, theLeastAncestor) (theGraphDVI(theGraph)[v].leastAncestor = theLeastAncestor)
#define gp_GetVertexLowpoint(theGraph, v) (theGraphDVI(theGraph)[v].lowpoint)
#define gp_SetVertexLowpoint(theGraph, v, theLowpoint) (theGraphDVI(theGraph)[v].lowpoint = theLowpoint)
+#define gp_GetVertexVisitedIndex(theGraph, v) (theGraphDVI(theGraph)[v].visitedIndex)
+#define gp_SetVertexVisitedIndex(theGraph, v, newVisitedIndex) (theGraphDVI(theGraph)[v].visitedIndex = newVisitedIndex)
+
#ifdef __cplusplus
}
#endif
diff --git a/planarity/c/graphLib/graphLib.h b/planarity/c/graphLib/graphLib.h
index b265194..0b7df15 100644
--- a/planarity/c/graphLib/graphLib.h
+++ b/planarity/c/graphLib/graphLib.h
@@ -52,7 +52,7 @@ extern "C"
// Tweak is for a non-functional revision (e.g. change of build scripts or testing code, user-facing string changes)
#define GP_PROJECTVERSION_MAJOR 5
-#define GP_PROJECTVERSION_MINOR 0
+#define GP_PROJECTVERSION_MINOR 1
#define GP_PROJECTVERSION_MAINT 0
#define GP_PROJECTVERSION_TWEAK 0
@@ -62,9 +62,9 @@ extern "C"
// shared library version numbers below.
//
// See configure.ac for how to update these version numbers
-#define GP_LIBPLANARITYVERSION_CURRENT 4
+#define GP_LIBPLANARITYVERSION_CURRENT 5
#define GP_LIBPLANARITYVERSION_REVISION 0
-#define GP_LIBPLANARITYVERSION_AGE 0
+#define GP_LIBPLANARITYVERSION_AGE 1
const char *gp_GetLibPlanarityVersionFull(void);
diff --git a/planarity/c/graphLib/homeomorphSearch/graphK23Search.c b/planarity/c/graphLib/homeomorphSearch/graphK23Search.c
index 69aa75c..232f905 100644
--- a/planarity/c/graphLib/homeomorphSearch/graphK23Search.c
+++ b/planarity/c/graphLib/homeomorphSearch/graphK23Search.c
@@ -9,7 +9,7 @@ See the LICENSE.TXT file for licensing information.
/* Imported functions */
-extern void _ClearAllVisitedFlagsInGraph(graphP);
+extern void _ClearAllVisitedFlagsInGraph(graphP theGraph);
extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
diff --git a/planarity/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c b/planarity/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c
index 17ea11a..6ec7c4e 100644
--- a/planarity/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c
+++ b/planarity/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c
@@ -29,8 +29,8 @@ int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
/* Forward declarations of functions used by the extension system */
void *_K23Search_DupContext(void *pContext, void *theGraph);
-int _K23Search_CopyData(void *, void *);
-void _K23Search_FreeContext(void *);
+int _K23Search_CopyData(void *dstContext, void *srcContext);
+void _K23Search_FreeContext(void *pContext);
/****************************************************************************
* K23SEARCH_ID - the variable used to hold the integer identifier for this
@@ -51,7 +51,7 @@ int gp_ExtendWith_K23Search(graphP theGraph)
{
K23SearchContext *context = NULL;
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// If the K2,3 search feature has already been attached to the graph
@@ -118,6 +118,9 @@ void *_K23Search_DupContext(void *pContext, void *theGraph)
K23SearchContext *context = (K23SearchContext *)pContext;
K23SearchContext *newContext = (K23SearchContext *)malloc(sizeof(K23SearchContext));
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)theGraph;
+
if (newContext != NULL)
{
*newContext = *context;
@@ -131,6 +134,11 @@ void *_K23Search_DupContext(void *pContext, void *theGraph)
********************************************************************/
int _K23Search_CopyData(void *dstContext, void *srcContext)
{
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)dstContext;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)srcContext;
+
return OK;
}
diff --git a/planarity/c/graphLib/homeomorphSearch/graphK33Search.c b/planarity/c/graphLib/homeomorphSearch/graphK33Search.c
index 28d6036..0d1b052 100644
--- a/planarity/c/graphLib/homeomorphSearch/graphK33Search.c
+++ b/planarity/c/graphLib/homeomorphSearch/graphK33Search.c
@@ -13,7 +13,7 @@ See the LICENSE.TXT file for licensing information.
extern int _ClearAllVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
extern int _ClearAllVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
extern void _ClearEdgeVisitedFlagsInUnembeddedEdges(graphP theGraph);
-extern int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue);
+extern int _FillVertexVisitedIndexesInBicomp(graphP theGraph, int BicompRoot, int FillValue);
// extern int _GetBicompSize(graphP theGraph, int BicompRoot);
extern int _HideInternalEdges(graphP theGraph, int vertex);
@@ -78,7 +78,6 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v,
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 _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);
@@ -205,10 +204,10 @@ int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int
if (_ReduceBicomp(theGraph, context, R) != OK)
return NOTOK;
- /* Set visitedInfo values in the bicomp to the initialized state so the planarity
+ /* Set visitedIndex 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, gp_GetN(theGraph)) != OK)
+ if (_FillVertexVisitedIndexesInBicomp(theGraph, IC->r, gp_GetN(theGraph)) != OK)
return NOTOK;
/* We now intend to ignore the pertinence of W (conceptually eliminating
@@ -824,13 +823,13 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v,
/* Switch the 'current step' variable v to be equal to the
non-virtual counterpart of the bicomp root. */
- IC->v = gp_GetVertexFromBicompRoot(theGraph, R);
+ IC->v = _gp_GetVertexFromBicompRoot(theGraph, R);
/* Reset the visitation, pertinence and future pertinence settings from step u_max for step v */
for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
{
- gp_SetVertexVisitedInfo(theGraph, v, gp_GetN(theGraph));
+ gp_SetVertexVisitedIndex(theGraph, v, gp_GetN(theGraph));
gp_SetVertexPertinentEdge(theGraph, v, NIL);
gp_SetVertexPertinentRootsList(theGraph, v, NIL);
@@ -928,12 +927,12 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v,
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
+ So, we reuse the visitedIndex 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
+ bicomp is reduced and the visitedIndex 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
@@ -941,7 +940,7 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v,
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
+ those already marked visited by this method (i.e. those with visitedIndex
of -1) and those with a known obstruction type.
We want to manage the stack so that it when the desired vertex is found,
@@ -950,7 +949,7 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v,
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
+ is unknown and whose visitedIndex 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
@@ -964,7 +963,7 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v,
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
+ visitedIndex 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
@@ -1003,7 +1002,7 @@ int _TestForZtoWPath(graphP theGraph)
// Mark this vertex as being visited by this method (i.e. ineligible
// to have processing started on it again)
- gp_SetVertexVisitedInfo(theGraph, v, -1);
+ gp_SetVertexVisitedIndex(theGraph, v, -1);
e = gp_GetFirstEdge(theGraph, v);
}
@@ -1021,7 +1020,7 @@ int _TestForZtoWPath(graphP theGraph)
// 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_GetVertexVisitedIndex(theGraph, w) != -1 &&
gp_GetObstructionMark(theGraph, w) == ANYVERTEX_OBSTRUCTIONMARK_UNMARKED)
{
sp_Push2(theGraph->theStack, v, e);
@@ -1408,22 +1407,6 @@ int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R)
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)
-{
- _K33Search_InitEdgeRec(context, e);
- _K33Search_InitEdgeRec(context, gp_GetTwin(theGraph, e));
-
- return gp_DeleteEdge(theGraph, e);
-}
-
/********************************************************************
_K33Search_DeleteUnmarkedEdgesInBicomp()
@@ -1435,7 +1418,7 @@ int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e)
per vertex in the bicomp.
This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls
- the overloaded _K33_DeleteEdge() rather than gp_DeleteEdge()
+ the overloaded gp_DeleteEdge()
Returns OK on success, NOTOK on implementation failure
********************************************************************/
@@ -1445,6 +1428,9 @@ int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *co
int V, e, eNext;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)context;
+
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
@@ -1458,7 +1444,7 @@ int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *co
eNext = gp_GetNextEdge(theGraph, e);
if (!gp_GetEdgeVisited(theGraph, e))
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
e = eNext;
}
}
@@ -1508,7 +1494,7 @@ int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, in
e = gp_GetFirstEdge(theGraph, u);
v = gp_GetNeighbor(theGraph, e);
}
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
e = gp_GetLastEdge(theGraph, x);
// An edge e is a reduction edge if it has a pathConnector vertex set
@@ -1519,7 +1505,7 @@ int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, in
e = gp_GetLastEdge(theGraph, x);
w = gp_GetNeighbor(theGraph, e);
}
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
/* 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
@@ -1572,7 +1558,7 @@ int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x
e = gp_GetNextEdge(theGraph, e);
v = gp_GetNeighbor(theGraph, e);
}
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
e = gp_GetFirstEdge(theGraph, x);
e = gp_GetNextEdge(theGraph, e);
@@ -1586,7 +1572,7 @@ int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x
e = gp_GetNextEdge(theGraph, e);
w = gp_GetNeighbor(theGraph, e);
}
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
/* Now add a single edge to represent the XY-path */
gp_InsertEdge(theGraph, u, gp_GetFirstEdge(theGraph, u), 0,
@@ -1647,7 +1633,7 @@ int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e)
/* 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 edge capacity. */
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
/* 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.
@@ -1732,7 +1718,7 @@ int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context)
/* 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 edge capacity. */
- _K33Search_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
/* 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.
@@ -1846,7 +1832,7 @@ int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, in
if (gp_IsVirtualVertex(theGraph, p))
{
gp_SetVisited(theGraph, p);
- p = gp_GetVertexFromBicompRoot(theGraph, p);
+ p = _gp_GetVertexFromBicompRoot(theGraph, p);
}
}
@@ -1878,7 +1864,7 @@ int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, in
if (gp_IsVirtualVertex(theGraph, p))
{
- p = gp_GetVertexFromBicompRoot(theGraph, p);
+ p = _gp_GetVertexFromBicompRoot(theGraph, p);
gp_ClearVisited(theGraph, p);
}
}
diff --git a/planarity/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c b/planarity/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c
index 5c1b5e3..3b35d5d 100644
--- a/planarity/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c
+++ b/planarity/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c
@@ -40,6 +40,7 @@ int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int W
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_DeleteEdge(graphP theGraph, int e);
int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
@@ -51,7 +52,7 @@ int _K33Search_EnsureEdgeCapacity(graphP theGraph, int requiredEdgeCapacity);
void *_K33Search_DupContext(void *pContext, void *theGraph);
int _K33Search_CopyData(void *dstContext, void *srcContext);
-void _K33Search_FreeContext(void *);
+void _K33Search_FreeContext(void *pContext);
/****************************************************************************
* K33SEARCH_ID - the variable used to hold the integer identifier for this
@@ -72,7 +73,7 @@ int gp_ExtendWith_K33Search(graphP theGraph)
{
K33SearchContext *context = NULL;
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// If the K3,3 search feature has already been attached to the graph,
@@ -117,6 +118,7 @@ int gp_ExtendWith_K33Search(graphP theGraph)
context->functions.fpEnsureVertexCapacity = _K33Search_EnsureVertexCapacity;
context->functions.fpResetGraphStorage = _K33Search_ResetGraphStorage;
context->functions.fpEnsureEdgeCapacity = _K33Search_EnsureEdgeCapacity;
+ context->functions.fpDeleteEdge = _K33Search_DeleteEdge;
_K33Search_ClearStructures(context);
@@ -784,6 +786,28 @@ int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
return NOTOK;
}
+/********************************************************************
+ 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 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, int e)
+{
+ K33SearchContext *context = NULL;
+ gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
+
+ if (context == NULL)
+ return NOTOK;
+
+ _K33Search_InitEdgeRec(context, e);
+ _K33Search_InitEdgeRec(context, gp_GetTwin(theGraph, e));
+
+ return context->functions.fpDeleteEdge(theGraph, e);
+}
+
/********************************************************************
********************************************************************/
diff --git a/planarity/c/graphLib/homeomorphSearch/graphK4Search.c b/planarity/c/graphLib/homeomorphSearch/graphK4Search.c
index 5fe8b65..ddffa6d 100644
--- a/planarity/c/graphLib/homeomorphSearch/graphK4Search.c
+++ b/planarity/c/graphLib/homeomorphSearch/graphK4Search.c
@@ -10,7 +10,7 @@ See the LICENSE.TXT file for licensing information.
/* Imported functions */
extern void _InitIsolatorContext(graphP theGraph);
-extern void _ClearAllVisitedFlagsInGraph(graphP);
+extern void _ClearAllVisitedFlagsInGraph(graphP theGraph);
extern int _ClearAllVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
// extern int _ClearAllVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
// extern void _ClearEdgeVisitedFlagsInUnembeddedEdges(graphP theGraph);
@@ -73,7 +73,6 @@ 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 _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e);
int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context);
@@ -460,7 +459,7 @@ int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R)
// 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_GetVertexFromBicompRoot(theGraph, R) != v)
+ if (_gp_GetVertexFromBicompRoot(theGraph, R) != v)
theGraphIC(theGraph)->minorType |= MINORTYPE_A;
// If W has a pertinent child bicomp, then we've found Minor B.
@@ -879,7 +878,7 @@ int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int
// 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, gp_GetN(theGraph));
+ gp_SetVertexVisitedIndex(theGraph, W, gp_GetN(theGraph));
return OK;
}
@@ -980,7 +979,7 @@ int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, in
// 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, gp_GetN(theGraph));
+ gp_SetVertexVisitedIndex(theGraph, A, gp_GetN(theGraph));
// Find the component's remaining edges e_A and e_R incident to A and R
ZPrevLink = prevLink;
@@ -1018,7 +1017,7 @@ int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, in
per vertex in the bicomp.
This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls
- the overloaded _K4_DeleteEdge() rather than gp_DeleteEdge()
+ the overloaded gp_DeleteEdge()
Returns OK on success, NOTOK on implementation failure
********************************************************************/
@@ -1028,6 +1027,9 @@ int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, i
int V, e, eNext;
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)context;
+
sp_Push(theGraph->theStack, BicompRoot);
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
{
@@ -1041,29 +1043,13 @@ int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, i
eNext = gp_GetNextEdge(theGraph, e);
if (!gp_GetEdgeVisited(theGraph, e))
- _K4_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
e = eNext;
}
}
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)
-{
- _K4Search_InitEdgeRec(context, e);
- _K4Search_InitEdgeRec(context, gp_GetTwin(theGraph, e));
-
- return gp_DeleteEdge(theGraph, e);
-}
-
/****************************************************************************
_K4_GetCumulativeOrientationOnDFSPath()
****************************************************************************/
@@ -1076,7 +1062,7 @@ int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int des
copy before starting the loop */
if (gp_IsVirtualVertex(theGraph, descendant))
- descendant = gp_GetVertexFromBicompRoot(theGraph, descendant);
+ descendant = _gp_GetVertexFromBicompRoot(theGraph, descendant);
while (descendant != ancestor)
{
@@ -1086,7 +1072,7 @@ int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int des
// If we are at a bicomp root, then ascend to its parent copy
if (gp_IsVirtualVertex(theGraph, descendant))
{
- parent = gp_GetVertexFromBicompRoot(theGraph, descendant);
+ parent = _gp_GetVertexFromBicompRoot(theGraph, descendant);
}
// If we are on a regular, non-virtual vertex then get the edge to the parent
@@ -1255,7 +1241,7 @@ int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink,
while (sp_NonEmpty(theGraph->theStack))
{
sp_Pop(theGraph->theStack, e);
- _K4_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
}
return OK;
@@ -1321,8 +1307,8 @@ int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType
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);
- _K4_DeleteEdge(theGraph, context, e_A);
+ gp_DeleteEdge(theGraph, e_R);
+ gp_DeleteEdge(theGraph, e_A);
// 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,
@@ -1401,7 +1387,7 @@ int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e)
// 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 edge capacity.
- _K4_DeleteEdge(theGraph, context, e);
+ gp_DeleteEdge(theGraph, e);
// 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.
diff --git a/planarity/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c b/planarity/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c
index 1646ba2..3c6e592 100644
--- a/planarity/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c
+++ b/planarity/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c
@@ -33,6 +33,7 @@ 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_DeleteEdge(graphP theGraph, int e);
int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
@@ -44,7 +45,7 @@ int _K4Search_EnsureEdgeCapacity(graphP theGraph, int requiredEdgeCapacity);
void *_K4Search_DupContext(void *pContext, void *theGraph);
int _K4Search_CopyData(void *dstContext, void *srcContext);
-void _K4Search_FreeContext(void *);
+void _K4Search_FreeContext(void *pContext);
/****************************************************************************
* K4SEARCH_ID - the variable used to hold the integer identifier for this
@@ -65,7 +66,7 @@ int gp_ExtendWith_K4Search(graphP theGraph)
{
K4SearchContext *context = NULL;
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// If the K4 search feature has already been attached to the graph,
@@ -105,6 +106,7 @@ int gp_ExtendWith_K4Search(graphP theGraph)
context->functions.fpEnsureVertexCapacity = _K4Search_EnsureVertexCapacity;
context->functions.fpResetGraphStorage = _K4Search_ResetGraphStorage;
context->functions.fpEnsureEdgeCapacity = _K4Search_EnsureEdgeCapacity;
+ context->functions.fpDeleteEdge = _K4Search_DeleteEdge;
_K4Search_ClearStructures(context);
@@ -524,6 +526,28 @@ int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
return NOTOK;
}
+/********************************************************************
+ Edge deletion that occurs during a reduction or restoration of a
+ reduction is augmented by clearing the K_4 search-specific
+ data members. This augmentation is not needed in the delete edge
+ operations that happen once a K_4 homeomorph has been found and
+ marked for isolation.
+ ********************************************************************/
+
+int _K4Search_DeleteEdge(graphP theGraph, int e)
+{
+ K4SearchContext *context = NULL;
+ gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
+
+ if (context == NULL)
+ return NOTOK;
+
+ _K4Search_InitEdgeRec(context, e);
+ _K4Search_InitEdgeRec(context, gp_GetTwin(theGraph, e));
+
+ return context->functions.fpDeleteEdge(theGraph, e);
+}
+
/********************************************************************
********************************************************************/
diff --git a/planarity/c/graphLib/io/g6-api-utilities.c b/planarity/c/graphLib/io/g6-api-utilities.c
index e1b6096..f8b809f 100644
--- a/planarity/c/graphLib/io/g6-api-utilities.c
+++ b/planarity/c/graphLib/io/g6-api-utilities.c
@@ -25,7 +25,7 @@ int _g6_ValidateOrderOfEncodedGraph(char *graphBuff, int order);
int _g6_ValidateGraphEncoding(char *graphBuff, const int order, const size_t numChars);
/* Private functions */
-size_t _g6_GetMaxEdgeCount(int);
+size_t _g6_GetMaxEdgeCount(int order);
size_t _g6_GetMaxEdgeCount(int order)
{
diff --git a/planarity/c/graphLib/io/g6-read-iterator.c b/planarity/c/graphLib/io/g6-read-iterator.c
index 61b8934..986bc55 100644
--- a/planarity/c/graphLib/io/g6-read-iterator.c
+++ b/planarity/c/graphLib/io/g6-read-iterator.c
@@ -32,7 +32,7 @@ int _g6_ValidateHeader(strOrFileP inputContainer);
int _g6_ValidateFirstChar(char c, const int lineNum);
int _g6_DetermineOrderFromInput(strOrFileP inputContainer, int *order);
-int _g6_DecodeGraph(char *graphBuff, const int order, const int numChars, graphP theGraph);
+int _g6_DecodeGraph(char *graphBuff, const int order, const size_t numChars, graphP theGraph);
int _g6_ReadGraphFromFile(graphP theGraph, char *pathToG6File);
int _g6_ReadGraphFromString(graphP theGraph, char *g6EncodedString);
@@ -249,7 +249,7 @@ int _g6_InitReaderWithStrOrFile(G6ReadIteratorP theG6ReadIterator, strOrFileP *p
int _g6_InitReader(G6ReadIteratorP theG6ReadIterator)
{
- char charConfirmation = EOF;
+ int charConfirmation = EOF;
int firstChar = '\0';
int lineNum = 1;
int order = NIL;
@@ -262,7 +262,7 @@ int _g6_InitReader(G6ReadIteratorP theG6ReadIterator)
}
else
{
- charConfirmation = sf_ungetc((char)firstChar, inputContainer);
+ charConfirmation = sf_ungetc(firstChar, inputContainer);
if (charConfirmation != firstChar)
{
@@ -283,7 +283,7 @@ int _g6_InitReader(G6ReadIteratorP theG6ReadIterator)
}
firstChar = sf_getc(inputContainer);
- charConfirmation = sf_ungetc((char)firstChar, inputContainer);
+ charConfirmation = sf_ungetc(firstChar, inputContainer);
if (charConfirmation != firstChar)
{
@@ -356,6 +356,7 @@ int _g6_InitReader(G6ReadIteratorP theG6ReadIterator)
int _g6_ValidateHeader(strOrFileP inputContainer)
{
+ int intChar = 0;
char const *g6Header = ">>graph6<<";
char const *sparse6Header = ">>sparse6<";
char const *digraph6Header = ">>digraph6";
@@ -371,7 +372,13 @@ int _g6_ValidateHeader(strOrFileP inputContainer)
for (int i = 0; i < 10; i++)
{
- headerCandidateChars[i] = sf_getc(inputContainer);
+ intChar = sf_getc(inputContainer);
+ if (intChar == EOF)
+ {
+ gp_ErrorMessage("Truncated >>graph6<< header.");
+ return NOTOK;
+ }
+ headerCandidateChars[i] = (char)intChar;
}
headerCandidateChars[10] = '\0';
@@ -430,7 +437,7 @@ int _g6_DetermineOrderFromInput(strOrFileP inputContainer, int *order)
return NOTOK;
}
- sf_ungetc((char)graphChar, inputContainer);
+ sf_ungetc(graphChar, inputContainer);
for (int i = 2; i >= 0; i--)
{
@@ -468,21 +475,26 @@ int g6_ReadGraph(G6ReadIteratorP theG6ReadIterator)
graphP currGraph = NULL;
const int order = theG6ReadIterator == NULL ? 0 : theG6ReadIterator->order;
const int numCharsForOrder = theG6ReadIterator == NULL ? 0 : theG6ReadIterator->numCharsForOrder;
- const int numCharsForGraphEncoding = theG6ReadIterator == NULL ? 0 : theG6ReadIterator->numCharsForGraphEncoding;
- const int currGraphBuffSize = theG6ReadIterator == NULL ? 0 : theG6ReadIterator->currGraphBuffSize;
+ const size_t numCharsForGraphEncoding = theG6ReadIterator == NULL ? 0 : theG6ReadIterator->numCharsForGraphEncoding;
+ const size_t currGraphBuffSize = theG6ReadIterator == NULL ? 0 : theG6ReadIterator->currGraphBuffSize;
if (!_g6_IsReaderInitialized(theG6ReadIterator, TRUE))
{
gp_ErrorMessage("G6ReadIterator is not initialized.");
return NOTOK;
}
+ if (numCharsForGraphEncoding > INT_MAX || currGraphBuffSize > INT_MAX)
+ {
+ gp_ErrorMessage("Integer overflow.");
+ return NOTOK;
+ }
inputContainer = theG6ReadIterator->inputContainer;
lineNum = theG6ReadIterator->numGraphsRead + 1;
currGraphBuff = theG6ReadIterator->currGraphBuff;
currGraph = theG6ReadIterator->currGraph;
- if (sf_fgets(currGraphBuff, currGraphBuffSize, inputContainer) != NULL)
+ if (sf_fgets(currGraphBuff, (int)currGraphBuffSize, inputContainer) != NULL)
{
firstChar = currGraphBuff[0];
@@ -496,10 +508,9 @@ int g6_ReadGraph(G6ReadIteratorP theG6ReadIterator)
// 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) != (((lineNum == 1) ? 0 : numCharsForOrder) + numCharsForGraphEncoding))
+ if (strlen(currGraphBuff) != (((lineNum == 1) ? 0 : numCharsForOrder) + numCharsForGraphEncoding))
{
- gp_ErrorMessage("Invalid line length read on line %d",
- lineNum);
+ gp_ErrorMessage("Invalid line length read on line %d", lineNum);
return NOTOK;
}
@@ -507,8 +518,7 @@ int g6_ReadGraph(G6ReadIteratorP theG6ReadIterator)
{
if (_g6_ValidateOrderOfEncodedGraph(currGraphBuff, order) != OK)
{
- gp_ErrorMessage("Order of graph on line %d is incorrect.",
- lineNum);
+ gp_ErrorMessage("Order of graph on line %d is incorrect.", lineNum);
return NOTOK;
}
}
@@ -557,9 +567,9 @@ int g6_ReadGraph(G6ReadIteratorP theG6ReadIterator)
// 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 _g6_DecodeGraph(char *graphBuff, const int order, const int numChars, graphP theGraph)
+int _g6_DecodeGraph(char *graphBuff, const int order, const size_t numChars, graphP theGraph)
{
- int numPaddingZeroes = _g6_GetExpectedNumPaddingZeroes(order, numChars);
+ size_t numPaddingZeroes = _g6_GetExpectedNumPaddingZeroes(order, numChars);
char currByte = '\0';
int bitValue = 0;
@@ -573,7 +583,13 @@ int _g6_DecodeGraph(char *graphBuff, const int order, const int numChars, graphP
return NOTOK;
}
- for (int i = 0; i < numChars; i++)
+ if (numChars > INT_MAX || numPaddingZeroes > INT_MAX)
+ {
+ gp_ErrorMessage("Integer overflow.");
+ return NOTOK;
+ }
+
+ for (int i = 0; i < (int)numChars; i++)
{
currByte = graphBuff[i] - 63;
// j corresponds to the number of places one must bitshift the byte by
@@ -582,7 +598,7 @@ int _g6_DecodeGraph(char *graphBuff, const int order, const int numChars, graphP
{
// 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)
+ if ((i == (int)numChars) && j == ((int)numPaddingZeroes) - 1)
break;
if (row == col)
diff --git a/planarity/c/graphLib/io/g6-write-iterator.c b/planarity/c/graphLib/io/g6-write-iterator.c
index 0b6d916..15ca66a 100644
--- a/planarity/c/graphLib/io/g6-write-iterator.c
+++ b/planarity/c/graphLib/io/g6-write-iterator.c
@@ -70,8 +70,7 @@ int g6_NewWriter(G6WriteIteratorP *pG6WriteIterator, graphP theGraph)
if (pG6WriteIterator != NULL && (*pG6WriteIterator) != NULL)
{
- gp_ErrorMessage("G6WriteIterator is not NULL and therefore can't be "
- "allocated.");
+ gp_ErrorMessage("G6WriteIterator is not NULL and therefore can't be allocated.");
return NOTOK;
}
@@ -116,8 +115,7 @@ int _g6_IsWriterInitialized(G6WriteIteratorP theG6WriteIterator, int reportUnini
if (!sf_IsValidStrOrFile(theG6WriteIterator->outputContainer))
{
if (reportUninitializedParts)
- gp_ErrorMessage("G6WriteIterator's outputContainer is not "
- "valid.");
+ gp_ErrorMessage("G6WriteIterator's outputContainer is not valid.");
writerIsInitialized = FALSE;
}
if (theG6WriteIterator->currGraphBuff == NULL)
@@ -159,16 +157,14 @@ int g6_InitWriterWithString(G6WriteIteratorP theG6WriteIterator, char **pOutputS
if (theG6WriteIterator == NULL)
{
- gp_ErrorMessage("Invalid parameter: theG6WriteIterator must be "
- "non-NULL.");
+ gp_ErrorMessage("Invalid parameter: theG6WriteIterator must be non-NULL.");
return NOTOK;
}
if (_g6_IsWriterInitialized(theG6WriteIterator, FALSE))
{
gp_ErrorMessage(
- "Unable to initialize writer, as it was already previously "
- "initialized.");
+ "Unable to initialize writer, as it was already previously initialized.");
return NOTOK;
}
@@ -205,8 +201,7 @@ int g6_InitWriterWithFileName(G6WriteIteratorP theG6WriteIterator, char *outputF
if (theG6WriteIterator == NULL)
{
- gp_ErrorMessage("Invalid parameter: theG6WriteIterator must be "
- "non-NULL.");
+ gp_ErrorMessage("Invalid parameter: theG6WriteIterator must be non-NULL.");
return NOTOK;
}
@@ -238,24 +233,35 @@ int g6_InitWriterWithFileName(G6WriteIteratorP theG6WriteIterator, char *outputF
int _g6_InitWriterWithStrOrFile(G6WriteIteratorP theG6WriteIterator, strOrFileP *pOutputContainer)
{
- if (theG6WriteIterator == NULL)
+ int Result = OK;
+
+ if (!sf_IsValidStrOrFile((*pOutputContainer)))
{
- gp_ErrorMessage("Invalid parameter: theG6WriteIterator must be "
- "non-NULL.");
+ gp_ErrorMessage("Unable to initialize writer with invalid strOrFile "
+ "output container.");
+ if (pOutputContainer != NULL && (*pOutputContainer) != NULL)
+ {
+ sf_SetOutputErrorFlag((*pOutputContainer));
+ sf_Free(pOutputContainer);
+ }
return NOTOK;
}
- if (_g6_IsWriterInitialized(theG6WriteIterator, FALSE))
+ if (theG6WriteIterator == NULL)
{
- gp_ErrorMessage("Unable to initialize writer, as it was already "
- "previously initialized.");
+ gp_ErrorMessage("Invalid parameter: theG6WriteIterator must be non-NULL.");
+ sf_SetOutputErrorFlag((*pOutputContainer));
+ sf_Free(pOutputContainer);
return NOTOK;
}
- if (!sf_IsValidStrOrFile((*pOutputContainer)))
+ if (_g6_IsWriterInitialized(theG6WriteIterator, FALSE))
{
- gp_ErrorMessage("Unable to initialize writer with invalid strOrFile "
- "output container.");
+ gp_ErrorMessage("Unable to initialize writer, as it was already "
+ "previously initialized.");
+ sf_SetOutputErrorFlag((*pOutputContainer));
+ sf_Free(pOutputContainer);
+ g6_SetOutputErrorFlag(theG6WriteIterator);
return NOTOK;
}
@@ -265,7 +271,17 @@ int _g6_InitWriterWithStrOrFile(G6WriteIteratorP theG6WriteIterator, strOrFileP
// output container.
(*pOutputContainer) = NULL;
- return _g6_InitWriter(theG6WriteIterator);
+ Result = _g6_InitWriter(theG6WriteIterator);
+ if (Result != OK)
+ g6_SetOutputErrorFlag(theG6WriteIterator);
+
+ return Result;
+}
+
+void g6_SetOutputErrorFlag(G6WriteIteratorP theG6WriteIterator)
+{
+ if (theG6WriteIterator != NULL && theG6WriteIterator->outputContainer != NULL)
+ sf_SetOutputErrorFlag(theG6WriteIterator->outputContainer);
}
int _g6_InitWriter(G6WriteIteratorP theG6WriteIterator)
@@ -276,8 +292,7 @@ int _g6_InitWriter(G6WriteIteratorP theG6WriteIterator)
if (theG6WriteIterator->order > 100000)
{
- gp_ErrorMessage("Graphs of order n > 100000 are not supported at this "
- "time.");
+ gp_ErrorMessage("Graphs of order n > 100000 are not supported at this time.");
return NOTOK;
}
@@ -325,8 +340,7 @@ void _g6_PrecomputeColumnOffsets(size_t *columnOffsets, int order)
{
if (columnOffsets == NULL)
{
- gp_ErrorMessage("Must allocate columnOffsets memory before "
- "precomputation.");
+ gp_ErrorMessage("Must allocate columnOffsets memory before precomputation.");
return;
}
@@ -339,10 +353,18 @@ void _g6_PrecomputeColumnOffsets(size_t *columnOffsets, int order)
int g6_WriteGraph(G6WriteIteratorP theG6WriteIterator)
{
char *graphEncodingChars = NULL;
+
if (!_g6_IsWriterInitialized(theG6WriteIterator, TRUE))
{
- gp_ErrorMessage("Unable to write graph because G6WriteIterator is not "
- "initialized.");
+ gp_ErrorMessage("Unable to write graph because G6WriteIterator is not initialized.");
+ g6_SetOutputErrorFlag(theG6WriteIterator);
+ return NOTOK;
+ }
+
+ if (gp_GetGraphFlags(theG6WriteIterator->currGraph) & GRAPHFLAGS_DIRECTEDEDGEDETECTED)
+ {
+ gp_ErrorMessage("G6 format doesn't support digraphs.");
+ g6_SetOutputErrorFlag(theG6WriteIterator);
return NOTOK;
}
@@ -350,23 +372,23 @@ int g6_WriteGraph(G6WriteIteratorP theG6WriteIterator)
if (_g6_ValidateOrderOfEncodedGraph(theG6WriteIterator->currGraphBuff, theG6WriteIterator->order) != OK)
{
- gp_ErrorMessage("Unable to write graph, as constructed encoding has "
- "incorrect order.");
+ gp_ErrorMessage("Unable to write graph, as constructed encoding has incorrect order.");
+ g6_SetOutputErrorFlag(theG6WriteIterator);
return NOTOK;
}
graphEncodingChars = theG6WriteIterator->currGraphBuff + theG6WriteIterator->numCharsForOrder;
if (_g6_ValidateGraphEncoding(graphEncodingChars, theG6WriteIterator->order, theG6WriteIterator->numCharsForGraphEncoding) != OK)
{
- gp_ErrorMessage("Unable to write graph, as constructed encoding is "
- "invalid.");
+ gp_ErrorMessage("Unable to write graph, as constructed encoding is invalid.");
+ g6_SetOutputErrorFlag(theG6WriteIterator);
return NOTOK;
}
if (_g6_WriteEncodedGraph(theG6WriteIterator) != OK)
{
- gp_ErrorMessage("Unable to write g6 encoded graph to output "
- "container.");
+ gp_ErrorMessage("Unable to write g6 encoded graph to output container.");
+ g6_SetOutputErrorFlag(theG6WriteIterator);
return NOTOK;
}
@@ -592,8 +614,7 @@ int _g6_WriteGraphToString(graphP theGraph, char **pOutputStr)
if ((outputContainer = sf_NewOutputContainer(pOutputStr, NULL)) == NULL)
{
- gp_ErrorMessage("Unable to allocate outputContainer to which to "
- "write.");
+ gp_ErrorMessage("Unable to allocate outputContainer to which to write.");
return NOTOK;
}
@@ -616,6 +637,7 @@ int _g6_WriteGraphToStrOrFile(graphP theGraph, strOrFileP *pOutputContainer)
if (g6_NewWriter((&theG6WriteIterator), theGraph) != OK)
{
gp_ErrorMessage("Unable to allocate G6WriteIterator.");
+ g6_SetOutputErrorFlag(theG6WriteIterator);
g6_FreeWriter((&theG6WriteIterator));
return NOTOK;
}
diff --git a/planarity/c/graphLib/io/g6-write-iterator.h b/planarity/c/graphLib/io/g6-write-iterator.h
index e9555a1..bfba5e4 100644
--- a/planarity/c/graphLib/io/g6-write-iterator.h
+++ b/planarity/c/graphLib/io/g6-write-iterator.h
@@ -24,6 +24,8 @@ extern "C"
int g6_InitWriterWithString(G6WriteIteratorP theG6WriteIterator, char **pOutputString);
int g6_InitWriterWithFileName(G6WriteIteratorP theG6WriteIterator, char *outputFileName);
+ void g6_SetOutputErrorFlag(G6WriteIteratorP theG6WriteIterator);
+
int g6_WriteGraph(G6WriteIteratorP theG6WriteIterator);
void g6_FreeWriter(G6WriteIteratorP *pG6WriteIterator);
diff --git a/planarity/c/graphLib/io/graphIO.c b/planarity/c/graphLib/io/graphIO.c
index 4ffaf7e..8d90207 100644
--- a/planarity/c/graphLib/io/graphIO.c
+++ b/planarity/c/graphLib/io/graphIO.c
@@ -24,6 +24,7 @@ See the LICENSE.TXT file for licensing information.
/* Imported functions */
extern int _g6_ReadGraphFromStrOrFile(graphP theGraph, strOrFileP *pG6InputContainer);
extern int _g6_WriteGraphToStrOrFile(graphP theGraph, strOrFileP *pOutputContainer);
+extern int _WriteGraphMLGraph(graphP theGraph, strOrFileP outputContainer);
/* Private functions (exported to system) */
@@ -133,7 +134,7 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
int ErrorCode = OK;
int N = 0, v = NIL, W = NIL, adjList = NIL, e = NIL, indexValue = NIL;
- int zeroBased = FALSE;
+ int zeroBased = FALSE, inputOffset = NIL, inputTerminatorLowerBound = NIL;
if (!sf_IsValidStrOrFile(inputContainer))
return NOTOK;
@@ -158,10 +159,10 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
if (gp_EnsureVertexCapacity(theGraph, N) != OK)
return NOTOK;
- // Clear the visited members of the vertices so they can be used
+ // Clear the index members of the vertices so they can be used
// during the adjacency list read operation
for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
- gp_SetVertexVisitedInfo(theGraph, v, NIL);
+ gp_SetIndex(theGraph, v, NIL);
// Do the adjacency list read operation for each vertex in order
for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
@@ -174,20 +175,22 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
if (sf_ReadSkipWhitespace(inputContainer) != OK)
return NOTOK;
- if (indexValue == 0 && v == gp_LowerBoundVertexStorage(theGraph))
- zeroBased = TRUE;
-
- // If we are reading a zero-based input file, then we have to add to the
- // indexValue for v the offset of the first vertex in storage, which is
- // usually 1 (because we compile with USE_1BASEDARRAYS by default) but
- // which may be 0 if this library was compiled with USE_0BASEDARRAYS.
- if (zeroBased)
- indexValue += gp_LowerBoundVertexStorage(theGraph);
+ if (v == gp_LowerBoundVertexStorage(theGraph))
+ {
+ // Infer whether the input file is zero-based or one-based from
+ // the first vertex label, then convert file labels to storage indexes.
+ // A zero-based file terminates adjacency lists with any negative
+ // value; a one-based file terminates them with any value below 1.
+ zeroBased = indexValue == 0;
+ inputOffset = gp_LowerBoundVertexStorage(theGraph) - (zeroBased ? 0 : 1);
+ inputTerminatorLowerBound = zeroBased ? 0 : 1;
+ }
- gp_SetIndex(theGraph, v, indexValue);
+ // Convert the file vertex label to the corresponding storage location.
+ indexValue += inputOffset;
// The vertices are expected to be in numeric ascending order
- if (gp_GetIndex(theGraph, v) != v)
+ if (indexValue != v)
return NOTOK;
// Skip the colon after the vertex number
@@ -207,14 +210,14 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
adjList = gp_GetFirstEdge(theGraph, v);
if (gp_IsEdge(theGraph, adjList))
{
- // Store the adjacency node location in the visited member of each
+ // Store the adjacency node location in the index 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_GetFirstEdge(theGraph, v);
while (gp_IsEdge(theGraph, e))
{
- gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), e);
+ gp_SetIndex(theGraph, gp_GetNeighbor(theGraph, e), e);
e = gp_GetNextEdge(theGraph, e);
}
@@ -238,17 +241,16 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
if (sf_ReadSkipWhitespace(inputContainer) != OK)
return NOTOK;
- // If we are reading a zero-based input file, then we have to add to W
- // the offset of the first vertex in storage, which is usually 1
- // (because we compile with USE_1BASEDARRAYS by default) but which may
- // be 0 if this library was compiled with USE_0BASEDARRAYS.
- if (zeroBased)
- W += gp_LowerBoundVertexStorage(theGraph);
+ // The adjacency list terminator belongs to the input numbering base,
+ // so detect it before converting labels to storage locations.
+ if (W < inputTerminatorLowerBound)
+ break;
+
+ W += inputOffset;
- // A value below the valid range indicates the adjacency list end
- // This was written before gp_IsNotVertex() existed
+ // A value outside the valid range is an error.
if (W < gp_LowerBoundVertices(theGraph))
- break;
+ return NOTOK;
// A value above the valid range is an error
if (W >= gp_UpperBoundVertices(theGraph))
@@ -273,12 +275,12 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
{
// If the directed edge already exists, then we add it
// as the new first edge of the vertex and delete it from adjList
- if (gp_IsEdge(theGraph, gp_GetVertexVisitedInfo(theGraph, W)))
+ if (gp_IsEdge(theGraph, gp_GetIndex(theGraph, W)))
{
- e = gp_GetVertexVisitedInfo(theGraph, W);
+ e = gp_GetIndex(theGraph, W);
// Remove the directed edge e from the adjList construct
- gp_SetVertexVisitedInfo(theGraph, W, NIL);
+ gp_SetIndex(theGraph, W, NIL);
if (adjList == e)
{
if ((adjList = gp_GetNextEdge(theGraph, e)) == e)
@@ -315,7 +317,7 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
{
e = adjList;
- gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), NIL);
+ gp_SetIndex(theGraph, gp_GetNeighbor(theGraph, e), NIL);
if ((adjList = gp_GetNextEdge(theGraph, e)) == e)
adjList = NIL;
@@ -332,6 +334,12 @@ int _ReadAdjList(graphP theGraph, strOrFileP inputContainer)
if (zeroBased)
theGraph->graphFlags |= GRAPHFLAGS_ZEROBASEDIO;
+ // The exit condition of this method is to have the index member of each
+ // non-virtual vertex v be equal to v, until overridden by a depth-first
+ // search (e.g., gp_DepthFirstSearch())
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ gp_SetIndex(theGraph, v, v);
+
return OK;
}
@@ -351,7 +359,7 @@ int _ReadLEDAGraph(graphP theGraph, strOrFileP inputContainer)
int graphType = 0;
int N = 0, M = 0, u = NIL, v = NIL;
- int zeroBasedOffset = gp_LowerBoundVertexStorage(theGraph) == ( 0 ) ? ( 1 ) : 0;
+ int zeroBasedOffset = (gp_LowerBoundVertexStorage(theGraph) == ( 0 )) ? ( 1 ) : ( 0 );
char Line[MAXLINE + 1];
memset(Line, '\0', (MAXLINE + 1));
@@ -548,7 +556,7 @@ int _ReadGraph(graphP theGraph, strOrFileP *pInputContainer)
// was OK.
if (extraDataAllowed)
{
- char charAfterGraphRead = EOF;
+ int charAfterGraphRead = EOF;
if ((charAfterGraphRead = sf_getc((*pInputContainer))) != EOF)
{
if (sf_ungetc(charAfterGraphRead, (*pInputContainer)) != charAfterGraphRead)
@@ -589,6 +597,11 @@ int _ReadGraph(graphP theGraph, strOrFileP *pInputContainer)
int _ReadPostprocess(graphP theGraph, char *extraData)
{
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)theGraph;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)extraData;
+
return OK;
}
@@ -687,6 +700,12 @@ int _WriteAdjMatrix(graphP theGraph, strOrFileP outputContainer)
if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
return NOTOK;
+ if (gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DIRECTEDEDGEDETECTED)
+ {
+ gp_ErrorMessage("Adjacency matrix writer does not support directed graphs.");
+ return NOTOK;
+ }
+
// Write the number of vertices in the graph to the file or string buffer
if (sprintf(numberStr, "%d\n", gp_GetN(theGraph)) < 1)
return NOTOK;
@@ -914,9 +933,10 @@ int _WriteDebugInfo(graphP theGraph, strOrFileP outputContainer)
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 writeMode
+ Pass WRITE_G6, WRITE_GRAPHML, WRITE_ADJLIST, WRITE_ADJMATRIX, or
+ WRITE_DEBUGINFO for writeMode.
- NOTE: For digraphs, it is an error to use a writeMode other than WRITE_ADJLIST
+ NOTE: For digraphs, only WRITE_ADJLIST and WRITE_GRAPHML are supported.
Returns NOTOK on error, OK on success.
********************************************************************/
@@ -950,10 +970,10 @@ int gp_Write(graphP theGraph, char const *fileName, int writeMode)
* 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 writeMode: WRITE_G6, WRITE_ADJLIST, or WRITE_ADJMATRIX
+ * on writeMode: WRITE_G6, WRITE_GRAPHML, WRITE_ADJLIST, or WRITE_ADJMATRIX
* (the WRITE_DEBUGINFO writeMode is not supported at this time)
- NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST
+ NOTE: For digraphs, only WRITE_ADJLIST and WRITE_GRAPHML are supported.
Returns NOTOK on error, or OK on success along with an allocated string
*pOutputStr that the caller must free()
@@ -974,21 +994,6 @@ int gp_WriteToString(graphP theGraph, char **pOutputStr, int writeMode)
sf_Free(&outputContainer);
- // NOTE: (#56) If an error was encountered when we _WriteGraph(), we do not
- // want to return garbage to the caller. When we free the output container,
- // if writing to string, this means that we will have taken the string from
- // the internal theStrBuf and have assigned it to the container's
- // pointer-pointer pOutputStr for output; if the RetVal is not OK, we
- // must free the string and set the pointer-pointer to NULL.
- if (RetVal != OK)
- {
- if (pOutputStr != NULL && (*pOutputStr) != NULL)
- {
- free((*pOutputStr));
- pOutputStr = NULL;
- }
- }
-
// NOTE: If the output string is NULL or empty, need to report NOTOK
if (pOutputStr != NULL && (*pOutputStr) == NULL)
RetVal = NOTOK;
@@ -1006,9 +1011,10 @@ int gp_WriteToString(graphP theGraph, char **pOutputStr, int writeMode)
_WriteGraph()
Writes theGraph into the strOrFile container.
- Pass WRITE_G6, WRITE_ADJLIST, WRITE_ADJMATRIX, or WRITE_DEBUGINFO for the Mode
+ Pass WRITE_G6, WRITE_GRAPHML, 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
+ NOTE: For digraphs, only WRITE_ADJLIST and WRITE_GRAPHML are supported.
Returns NOTOK on error, OK on success.
********************************************************************/
@@ -1016,6 +1022,7 @@ int gp_WriteToString(graphP theGraph, char **pOutputStr, int writeMode)
int _WriteGraph(graphP theGraph, strOrFileP *pOutputContainer, int Mode)
{
int RetVal = OK;
+ int extraDataAllowed = FALSE;
switch (Mode)
{
@@ -1026,19 +1033,26 @@ int _WriteGraph(graphP theGraph, strOrFileP *pOutputContainer, int Mode)
break;
case WRITE_ADJLIST:
RetVal = _WriteAdjList(theGraph, (*pOutputContainer));
+ if (RetVal == OK)
+ extraDataAllowed = TRUE;
break;
case WRITE_ADJMATRIX:
RetVal = _WriteAdjMatrix(theGraph, (*pOutputContainer));
+ if (RetVal == OK)
+ extraDataAllowed = TRUE;
break;
case WRITE_DEBUGINFO:
RetVal = _WriteDebugInfo(theGraph, (*pOutputContainer));
break;
+ case WRITE_GRAPHML:
+ RetVal = _WriteGraphMLGraph(theGraph, (*pOutputContainer));
+ break;
default:
RetVal = NOTOK;
break;
}
- if (RetVal == OK)
+ if (extraDataAllowed)
{
char *extraData = NULL;
@@ -1054,6 +1068,9 @@ int _WriteGraph(graphP theGraph, strOrFileP *pOutputContainer, int Mode)
}
}
+ if (RetVal != OK && pOutputContainer != NULL && (*pOutputContainer) != NULL)
+ sf_SetOutputErrorFlag((*pOutputContainer));
+
return RetVal;
}
@@ -1065,5 +1082,10 @@ int _WriteGraph(graphP theGraph, strOrFileP *pOutputContainer, int Mode)
int _WritePostprocess(graphP theGraph, char **pExtraData)
{
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)theGraph;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)pExtraData;
+
return OK;
}
diff --git a/planarity/c/graphLib/io/graphIO.h b/planarity/c/graphLib/io/graphIO.h
index 0701bd0..443c930 100644
--- a/planarity/c/graphLib/io/graphIO.h
+++ b/planarity/c/graphLib/io/graphIO.h
@@ -25,6 +25,7 @@ extern "C"
#define WRITE_ADJMATRIX 2
#define WRITE_DEBUGINFO 3
#define WRITE_G6 4
+#define WRITE_GRAPHML 5
// Graph Flags: see gp_GetGraphFlags()
// GRAPHFLAGS_ZEROBASEDIO is typically set by gp_Read() to indicate that the
diff --git a/planarity/c/graphLib/io/graphML-writer.c b/planarity/c/graphLib/io/graphML-writer.c
new file mode 100644
index 0000000..64c92b0
--- /dev/null
+++ b/planarity/c/graphLib/io/graphML-writer.c
@@ -0,0 +1,258 @@
+/*
+Copyright (c) 1997-2026, John M. Boyer
+All rights reserved.
+See the LICENSE.TXT file for licensing information.
+*/
+
+#include "graphIO.h"
+#include "strOrFile.h"
+
+/* Private functions (exported to system) */
+int _WriteGraphMLGraph(graphP theGraph, strOrFileP outputContainer);
+int _WriteGraphMLStartTag(strOrFileP outputContainer);
+int _WriteGraphMLKeysForGraph(graphP theGraph, strOrFileP outputContainer);
+int _WriteGraphMLGraphElement(graphP theGraph, int index, strOrFileP outputContainer);
+int _WriteGraphMLEndTag(strOrFileP outputContainer);
+int _WriteGraphMLGraphStartTag(graphP theGraph, int index, strOrFileP outputContainer);
+int _WriteGraphMLGraphCustomAttributes(graphP theGraph, strOrFileP outputContainer);
+int _WriteGraphMLGraphVertices(graphP theGraph, strOrFileP outputContainer);
+int _WriteGraphMLGraphEdges(graphP theGraph, strOrFileP outputContainer);
+int _WriteGraphMLGraphEndTag(graphP theGraph, strOrFileP outputContainer);
+
+/********************************************************************
+ _WriteGraphMLGraph()
+
+ Writes a single graph as a GraphML document.
+
+ Returns OK on success, NOTOK on failure.
+ ********************************************************************/
+int _WriteGraphMLGraph(graphP theGraph, strOrFileP outputContainer)
+{
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ if (_WriteGraphMLStartTag(outputContainer) != OK ||
+ _WriteGraphMLKeysForGraph(theGraph, outputContainer) != OK ||
+ _WriteGraphMLGraphElement(theGraph, 1, outputContainer) != OK ||
+ _WriteGraphMLEndTag(outputContainer) != OK)
+ return NOTOK;
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLKeysForGraph()
+
+ Writes GraphML key declarations for graph-level custom attributes.
+ ********************************************************************/
+int _WriteGraphMLKeysForGraph(graphP theGraph, strOrFileP outputContainer)
+{
+ char const *zeroBasedIOKey =
+ " \n"
+ " false\n"
+ " \n";
+
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ if ((gp_GetGraphFlags(theGraph) & GRAPHFLAGS_ZEROBASEDIO) &&
+ sf_fputs(zeroBasedIOKey, outputContainer) == EOF)
+ return NOTOK;
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLStartTag()
+
+ Writes the XML declaration and GraphML root start tag.
+ ********************************************************************/
+int _WriteGraphMLStartTag(strOrFileP outputContainer)
+{
+ char const *startTag =
+ "\n"
+ "\n";
+
+ return sf_fputs(startTag, outputContainer) == EOF ? NOTOK : OK;
+}
+
+/********************************************************************
+ _WriteGraphMLEndTag()
+
+ Writes the GraphML root end tag.
+ ********************************************************************/
+int _WriteGraphMLEndTag(strOrFileP outputContainer)
+{
+ return sf_fputs("\n", outputContainer) == EOF ? NOTOK : OK;
+}
+
+/********************************************************************
+ _WriteGraphMLGraphElement()
+
+ Writes a graph element and its node and edge children.
+ ********************************************************************/
+int _WriteGraphMLGraphElement(graphP theGraph, int index, strOrFileP outputContainer)
+{
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ if (_WriteGraphMLGraphStartTag(theGraph, index, outputContainer) != OK ||
+ _WriteGraphMLGraphCustomAttributes(theGraph, outputContainer) != OK ||
+ _WriteGraphMLGraphVertices(theGraph, outputContainer) != OK ||
+ _WriteGraphMLGraphEdges(theGraph, outputContainer) != OK ||
+ _WriteGraphMLGraphEndTag(theGraph, outputContainer) != OK)
+ return NOTOK;
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLGraphStartTag()
+
+ Writes the graph element start tag.
+ ********************************************************************/
+int _WriteGraphMLGraphStartTag(graphP theGraph, int index, strOrFileP outputContainer)
+{
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ if (sf_fputs(" \n",
+ outputContainer) == EOF)
+ return NOTOK;
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLGraphCustomAttributes()
+
+ Writes graph-level custom attribute values.
+ ********************************************************************/
+int _WriteGraphMLGraphCustomAttributes(graphP theGraph, strOrFileP outputContainer)
+{
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ if ((gp_GetGraphFlags(theGraph) & GRAPHFLAGS_ZEROBASEDIO) &&
+ sf_fputs(" true\n", outputContainer) == EOF)
+ return NOTOK;
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLGraphVertices()
+
+ Writes one node element for every graph vertex.
+ ********************************************************************/
+int _WriteGraphMLGraphVertices(graphP theGraph, strOrFileP outputContainer)
+{
+ int v = NIL;
+ int vertexOffset = 0;
+
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ vertexOffset = gp_LowerBoundVertexStorage(theGraph);
+
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ {
+ if (sf_fputs(" \n", outputContainer) == EOF)
+ return NOTOK;
+ }
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLGraphEdges()
+
+ Writes one edge element for every in-use edge pair.
+ ********************************************************************/
+int _WriteGraphMLGraphEdges(graphP theGraph, strOrFileP outputContainer)
+{
+ int e = NIL;
+ int edgeID = 0;
+ int sourceEdge = NIL;
+ int targetEdge = NIL;
+ int sourceVertex = NIL;
+ int targetVertex = NIL;
+ int vertexOffset = 0;
+
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ vertexOffset = gp_LowerBoundVertexStorage(theGraph);
+
+ for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); e += 2)
+ {
+ if (!gp_EdgeInUse(theGraph, e))
+ continue;
+
+ sourceEdge = e;
+ targetEdge = gp_GetTwin(theGraph, e);
+
+ if (gp_GetDirection(theGraph, sourceEdge) == EDGEFLAG_DIRECTION_INONLY)
+ {
+ sourceEdge = targetEdge;
+ targetEdge = e;
+ }
+ else if (gp_GetDirection(theGraph, sourceEdge) == 0 &&
+ gp_GetNeighbor(theGraph, targetEdge) > gp_GetNeighbor(theGraph, sourceEdge))
+ {
+ sourceEdge = targetEdge;
+ targetEdge = e;
+ }
+
+ sourceVertex = gp_GetNeighbor(theGraph, targetEdge) - vertexOffset;
+ targetVertex = gp_GetNeighbor(theGraph, sourceEdge) - vertexOffset;
+
+ if (sf_fputs(" \n", outputContainer) == EOF)
+ return NOTOK;
+
+ edgeID++;
+ }
+
+ return OK;
+}
+
+/********************************************************************
+ _WriteGraphMLGraphEndTag()
+
+ Writes the graph element end tag.
+ ********************************************************************/
+int _WriteGraphMLGraphEndTag(graphP theGraph, strOrFileP outputContainer)
+{
+ if (theGraph == NULL || !sf_IsValidStrOrFile(outputContainer))
+ return NOTOK;
+
+ return sf_fputs(" \n", outputContainer) == EOF ? NOTOK : OK;
+}
diff --git a/planarity/c/graphLib/io/strOrFile.c b/planarity/c/graphLib/io/strOrFile.c
index 846761e..07525d7 100644
--- a/planarity/c/graphLib/io/strOrFile.c
+++ b/planarity/c/graphLib/io/strOrFile.c
@@ -12,6 +12,51 @@ See the LICENSE.TXT file for licensing information.
#include "../lowLevelUtils/appconst.h"
#include "strOrFile.h"
+char *_sf_DuplicateString(char const *const str);
+int _sf_ShouldRemoveFailedOutputFile(strOrFileP theStrOrFile);
+int _sf_RemoveFailedOutputFile(strOrFileP theStrOrFile);
+
+char *_sf_DuplicateString(char const *const str)
+{
+ char *copy = NULL;
+ size_t strLen = 0;
+
+ if (str == NULL)
+ return NULL;
+
+ strLen = strlen(str) + 1;
+ copy = (char *)malloc(strLen * sizeof(char));
+ if (copy != NULL)
+ memcpy(copy, str, strLen);
+
+ return copy;
+}
+
+int _sf_ShouldRemoveFailedOutputFile(strOrFileP theStrOrFile)
+{
+ // NOTE: (#56) Used for spot-testing removal of output files upon error.
+ // return FALSE;
+ return (theStrOrFile != NULL &&
+ theStrOrFile->containerType == OUTPUT_CONTAINER &&
+ theStrOrFile->outputErrorFlag &&
+ theStrOrFile->fileName != NULL &&
+ strcmp(theStrOrFile->fileName, "stdout") != 0 &&
+ strcmp(theStrOrFile->fileName, "stderr") != 0 &&
+ theStrOrFile->pFile != NULL &&
+ theStrOrFile->fileMode != NULL &&
+ strcmp(theStrOrFile->fileMode, WRITETEXT) == 0)
+ ? TRUE
+ : FALSE;
+}
+
+int _sf_RemoveFailedOutputFile(strOrFileP theStrOrFile)
+{
+ if (!_sf_ShouldRemoveFailedOutputFile(theStrOrFile))
+ return OK;
+
+ return remove(theStrOrFile->fileName) == 0 ? OK : NOTOK;
+}
+
/********************************************************************
sf_NewInputContainer()
@@ -58,7 +103,11 @@ strOrFileP sf_NewInputContainer(char const *const inputStr, char const *const fi
}
else
{
- if ((theStrOrFile->pFile = fopen(fileName, READTEXT)) == NULL)
+ theStrOrFile->fileMode = READTEXT;
+ theStrOrFile->fileName = _sf_DuplicateString(fileName);
+
+ if (theStrOrFile->fileName == NULL ||
+ (theStrOrFile->pFile = fopen(fileName, READTEXT)) == NULL)
{
sf_Free(&theStrOrFile);
theStrOrFile = NULL;
@@ -141,12 +190,21 @@ strOrFileP sf_NewOutputContainer(char **pOutputStr, char const *const fileName)
return NULL;
}
else if (strcmp(fileName, "stdout") == 0)
+ {
theStrOrFile->pFile = stdout;
+ theStrOrFile->fileMode = WRITETEXT;
+ }
else if (strcmp(fileName, "stderr") == 0)
+ {
theStrOrFile->pFile = stderr;
+ theStrOrFile->fileMode = WRITETEXT;
+ }
else
{
- if (
+ theStrOrFile->fileMode = WRITETEXT;
+ theStrOrFile->fileName = _sf_DuplicateString(fileName);
+
+ if (theStrOrFile->fileName == NULL ||
(theStrOrFile->pFile = fopen(fileName, WRITETEXT)) == NULL)
{
sf_Free(&theStrOrFile);
@@ -199,7 +257,6 @@ int sf_IsValidStrOrFile(strOrFileP theStrOrFile)
if (
(theStrOrFile->ungetBuf == NULL) ||
(theStrOrFile->theStrBuf != NULL && sb_GetSize(theStrOrFile->theStrBuf) == 0))
-
{
return FALSE;
}
@@ -222,11 +279,17 @@ int sf_IsValidStrOrFile(strOrFileP theStrOrFile)
If the ungetBuf is empty, then we'll read from pFile using getc() OR
from theStrBuf by fetching the character at theStrPos and incrementing
theStrPos.
+
+ Like getc() in stdio, returns the character as an unsigned char
+ converted to int, or EOF. The int return type is what keeps EOF
+ distinguishable from the byte 0xFF on builds where plain char is
+ unsigned, e.g. the Linux ABIs for AArch64, ARM, PowerPC and s390x,
+ where (char)EOF == 255 (issue #319).
********************************************************************/
-char sf_getc(strOrFileP theStrOrFile)
+int sf_getc(strOrFileP theStrOrFile)
{
- char theChar = EOF;
+ int theChar = EOF;
if (!sf_IsValidStrOrFile(theStrOrFile) ||
theStrOrFile->containerType != INPUT_CONTAINER)
@@ -245,14 +308,16 @@ char sf_getc(strOrFileP theStrOrFile)
// so we cut to the underlying debug method to stop its warnings
sp__Pop(theStrOrFile->ungetBuf, &(currChar));
#endif
- theChar = (char)currChar;
+ theChar = currChar;
}
else if (theStrOrFile->pFile != NULL)
- theChar = (char)getc(theStrOrFile->pFile);
+ theChar = getc(theStrOrFile->pFile);
else if (theStrOrFile->theStrBuf != NULL && sb_GetUnreadCharCount(theStrOrFile->theStrBuf) > 0)
{
+ // N.B. Convert through unsigned char so that a literal 0xFF byte in
+ // the string buffer does not sign-extend to EOF on signed-char platforms
theChar = sb_GetReadString(theStrOrFile->theStrBuf) != NULL
- ? sb_GetReadString(theStrOrFile->theStrBuf)[0]
+ ? (unsigned char)sb_GetReadString(theStrOrFile->theStrBuf)[0]
: EOF;
if (theChar != EOF)
sb_ReadSkipChar(theStrOrFile->theStrBuf);
@@ -288,7 +353,7 @@ int sf_ReadSkipChar(strOrFileP theStrOrFile)
int sf_ReadSkipWhitespace(strOrFileP theStrOrFile)
{
- char currChar = EOF;
+ int currChar = EOF;
if (!sf_IsValidStrOrFile(theStrOrFile) ||
theStrOrFile->containerType != INPUT_CONTAINER)
@@ -348,7 +413,7 @@ int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile)
int exitCode = OK;
int intCandidate = 0, intCandidateIndex = 0;
- char currChar = '\0', nextChar = '\0';
+ int currChar = '\0', nextChar = '\0';
int startedReadingInt = FALSE, isNegative = FALSE;
char intCandidateStr[MAXCHARSFOR32BITINT + 1];
memset(intCandidateStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char));
@@ -383,14 +448,14 @@ int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile)
}
else
{
- intCandidateStr[intCandidateIndex++] = currChar;
+ intCandidateStr[intCandidateIndex++] = (char)currChar;
isNegative = TRUE;
}
}
}
else if (isdigit(currChar))
{
- intCandidateStr[intCandidateIndex++] = currChar;
+ intCandidateStr[intCandidateIndex++] = (char)currChar;
startedReadingInt = TRUE;
}
else
@@ -432,7 +497,7 @@ int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile)
if (exitCode == OK)
{
- intCandidateStr[intCandidateIndex++] = nextChar;
+ intCandidateStr[intCandidateIndex++] = (char)nextChar;
}
}
else if (sf_ungetc(nextChar, theStrOrFile) != nextChar)
@@ -495,17 +560,23 @@ int sf_ReadSkipLineRemainder(strOrFileP theStrOrFile)
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.
+ Like ungetc() in stdio, on success the pushed byte is returned as
+ (unsigned char)theChar converted to int. On failure, EOF is
+ returned. Takes and returns int for the same reason as sf_getc():
+ EOF must stay distinguishable from the byte 0xFF.
********************************************************************/
-char sf_ungetc(char theChar, strOrFileP theStrOrFile)
+int sf_ungetc(int theChar, strOrFileP theStrOrFile)
{
if (theChar == EOF ||
!sf_IsValidStrOrFile(theStrOrFile) ||
theStrOrFile->containerType != INPUT_CONTAINER ||
sp_GetCurrentSize(theStrOrFile->ungetBuf) >= sp_GetCapacity(theStrOrFile->ungetBuf))
- return EOF; // Acceptable downcast, allowing char rather than int return type
+ return EOF;
+
+ // N.B. Store the byte value as an unsigned char would deliver it, so a
+ // pushed-back byte reads back from sf_getc() exactly as it was read
+ theChar = (unsigned char)theChar;
#ifndef DEBUG
sp_Push(theStrOrFile->ungetBuf, theChar);
@@ -530,13 +601,18 @@ char sf_ungetc(char theChar, strOrFileP theStrOrFile)
int sf_ungets(char *strToUnget, strOrFileP theStrOrFile)
{
+ if (strToUnget == NULL || strlen(strToUnget) > INT_MAX)
+ return NOTOK;
+
if (!sf_IsValidStrOrFile(theStrOrFile) ||
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]);
+ for (int i = ((int)strlen(strToUnget) - 1); i >= 0; i--)
+ // N.B. Convert through unsigned char so a 0xFF byte does not enter the
+ // unget buffer as EOF on signed-char platforms
+ sp_Push(theStrOrFile->ungetBuf, (unsigned char)strToUnget[i]);
return OK;
}
@@ -573,7 +649,7 @@ char *sf_fgets(char *str, int count, strOrFileP theStrOrFile)
int numCharsInUngetBuf = sp_GetCurrentSize(theStrOrFile->ungetBuf);
if (numCharsInUngetBuf > 0)
{
- char currChar = '\0';
+ int currChar = '\0';
int encounteredNewline = FALSE;
charsToReadFromUngetBuf = (count > numCharsInUngetBuf) ? numCharsInUngetBuf : count;
@@ -582,7 +658,7 @@ char *sf_fgets(char *str, int count, strOrFileP theStrOrFile)
currChar = sf_getc(theStrOrFile);
if (currChar == EOF)
return NULL;
- str[i] = currChar;
+ str[i] = (char)currChar;
str[i + 1] = '\0';
// N.B. fgets() includes the \n in the string returned, and
// no further characters shall be read
@@ -649,9 +725,14 @@ int sf_fputs(char const *strToWrite, strOrFileP theStrOrFile)
int outputLen = EOF;
if (strToWrite == NULL ||
+ strlen(strToWrite) > INT_MAX ||
!sf_IsValidStrOrFile(theStrOrFile) ||
theStrOrFile->containerType != OUTPUT_CONTAINER)
+ {
+ gp_ErrorMessage("Internal error.");
+ sf_SetOutputErrorFlag(theStrOrFile);
return EOF;
+ }
// N.B. fputs() will fail and return EOF if pFile doesn't correspond
// to an output stream
@@ -660,14 +741,66 @@ int sf_fputs(char const *strToWrite, strOrFileP theStrOrFile)
else if (theStrOrFile->theStrBuf != NULL)
{
if (sb_ConcatString(theStrOrFile->theStrBuf, strToWrite) == OK)
- outputLen = strlen(strToWrite);
+ outputLen = (int)strlen(strToWrite);
else
outputLen = EOF;
}
+ if (outputLen == EOF)
+ sf_SetOutputErrorFlag(theStrOrFile);
+
return outputLen;
}
+/********************************************************************
+ sf_WriteInteger()
+
+ Writes an integer to a string-or-file output container.
+
+ Returns OK on success, NOTOK on failure.
+ ********************************************************************/
+
+int sf_WriteInteger(int intToWrite, strOrFileP theStrOrFile)
+{
+ int result = OK;
+
+ if (!sf_IsValidStrOrFile(theStrOrFile) ||
+ theStrOrFile->containerType != OUTPUT_CONTAINER)
+ {
+ gp_ErrorMessage("Internal error.");
+ sf_SetOutputErrorFlag(theStrOrFile);
+ return NOTOK;
+ }
+
+ if (theStrOrFile->pFile != NULL)
+ result = fprintf(theStrOrFile->pFile, "%d", intToWrite) < 0 ? NOTOK : OK;
+ else if (theStrOrFile->theStrBuf != NULL)
+ result = sb_ConcatInt(theStrOrFile->theStrBuf, intToWrite);
+
+ if (result != OK)
+ sf_SetOutputErrorFlag(theStrOrFile);
+
+ return result;
+}
+
+/********************************************************************
+ sf_SetOutputErrorFlag()
+
+ Marks the container as having encountered an error while reading or writing.
+ Output containers use this state during sf_Free() to avoid returning partial
+ strings and to remove partial ordinary output files.
+ ********************************************************************/
+
+int sf_SetOutputErrorFlag(strOrFileP theStrOrFile)
+{
+ if (theStrOrFile == NULL)
+ return NOTOK;
+
+ theStrOrFile->outputErrorFlag = TRUE;
+
+ return OK;
+}
+
/********************************************************************
sf_closeFile()
@@ -683,8 +816,13 @@ int sf_fputs(char const *strToWrite, strOrFileP theStrOrFile)
int sf_closeFile(strOrFileP theStrOrFile)
{
- FILE *pFile = theStrOrFile->pFile;
- theStrOrFile->pFile = NULL;
+ int closeResult = OK;
+ FILE *pFile = NULL;
+
+ if (theStrOrFile == NULL)
+ return NOTOK;
+
+ pFile = theStrOrFile->pFile;
if (pFile != NULL)
{
int errorCode = 0;
@@ -695,12 +833,32 @@ int sf_closeFile(strOrFileP theStrOrFile)
errorCode = fclose(pFile);
if (errorCode < 0)
- return NOTOK;
+ {
+ theStrOrFile->outputErrorFlag = TRUE;
+ closeResult = NOTOK;
+ }
+
+ // If the outputErrorFlag was already set on the output container, or if
+ // an error was encountered when trying to fclose() the file, then the
+ // output file must be removed. However, the behaviour of what happens
+ // when you call remove() after fclose() fails is platform-dependent: on
+ // Windows, remove() will fail and the output file will still persist,
+ // whereas on POSIX systems, the file will only persist on disk until
+ // any processes with open file descriptors (i.e. should only be this
+ // thread) terminate.
+ if (_sf_RemoveFailedOutputFile(theStrOrFile) != OK)
+ closeResult = NOTOK;
+
+ // NOTE: This indicates that we no longer own the FILE *, but we've
+ // delayed to this point because the FILE * tells us whether we've
+ // successfully opened the file, which is part of how we determine
+ // whether or not to remove() the file in an error case.
+ theStrOrFile->pFile = NULL;
}
sp_Free(&(theStrOrFile->ungetBuf));
- return OK;
+ return closeResult;
}
/********************************************************************
@@ -728,30 +886,32 @@ void sf_Free(strOrFileP *pStrOrFile)
{
if ((*pStrOrFile)->theStrBuf != NULL)
{
- // TODO: (#56) If in an error state, just don't sb_TakeString()
- // before freeing it
- if ((*pStrOrFile)->pOutputStr != NULL)
- {
+ // In an error state, discard the partial string instead of
+ // returning it to the caller.
+ if ((*pStrOrFile)->pOutputStr != NULL && !(*pStrOrFile)->outputErrorFlag)
(*((*pStrOrFile)->pOutputStr)) = sb_TakeString((*pStrOrFile)->theStrBuf);
- (*pStrOrFile)->pOutputStr = NULL;
- }
+
+ (*pStrOrFile)->pOutputStr = NULL;
+
sb_Free(&((*pStrOrFile)->theStrBuf));
}
- // 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;
+ if ((*pStrOrFile)->fileName != NULL)
+ {
+ free((*pStrOrFile)->fileName);
+ (*pStrOrFile)->fileName = NULL;
+ }
+
+ (*pStrOrFile)->fileMode = NULL;
+
free(*pStrOrFile);
(*pStrOrFile) = NULL;
}
diff --git a/planarity/c/graphLib/io/strOrFile.h b/planarity/c/graphLib/io/strOrFile.h
index 6ecf95c..8d57840 100644
--- a/planarity/c/graphLib/io/strOrFile.h
+++ b/planarity/c/graphLib/io/strOrFile.h
@@ -24,7 +24,10 @@ extern "C"
char **pOutputStr;
strBufP theStrBuf;
FILE *pFile;
+ char *fileName;
+ char const *fileMode;
int containerType;
+ int outputErrorFlag;
stackP ungetBuf;
};
@@ -36,7 +39,7 @@ extern "C"
int sf_IsValidStrOrFile(strOrFileP theStrOrFile);
- char sf_getc(strOrFileP theStrOrFile);
+ int sf_getc(strOrFileP theStrOrFile);
int sf_ReadSkipChar(strOrFileP theStrOrFile);
int sf_ReadSkipWhitespace(strOrFileP theStrOrFile);
int sf_ReadSingleDigit(int *digitToRead, strOrFileP theStrOrFile);
@@ -44,12 +47,15 @@ extern "C"
int sf_ReadSkipInteger(strOrFileP theStrOrFile);
int sf_ReadSkipLineRemainder(strOrFileP theStrOrFile);
- char sf_ungetc(char theChar, strOrFileP theStrOrFile);
+ int sf_ungetc(int 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);
+ int sf_WriteInteger(int intToWrite, strOrFileP theStrOrFile);
+
+ int sf_SetOutputErrorFlag(strOrFileP theStrOrFile);
int sf_closeFile(strOrFileP theStrOrFile);
diff --git a/planarity/c/graphLib/io/strbuf.c b/planarity/c/graphLib/io/strbuf.c
index b344676..c7f8c2f 100644
--- a/planarity/c/graphLib/io/strbuf.c
+++ b/planarity/c/graphLib/io/strbuf.c
@@ -5,9 +5,11 @@ See the LICENSE.TXT file for licensing information.
*/
#include "../lowLevelUtils/appconst.h"
+#include "../lowLevelUtils/apiutils.h"
#include "strbuf.h"
#include
#include
+#include
/********************************************************************
sb_New()
@@ -20,7 +22,7 @@ strBufP sb_New(int capacity)
{
strBufP theStrBuf;
- if (capacity < 0)
+ if (capacity < 0 || capacity == INT_MAX)
return NULL;
theStrBuf = (strBufP)malloc(sizeof(strBuf));
@@ -182,30 +184,43 @@ void sb_ReadSkipInteger(strBufP theStrBuf)
********************************************************************/
int sb_ConcatString(strBufP theStrBuf, char const *s)
{
- int slen = s == NULL ? 0 : strlen(s);
+ // Switched to size_t to fix int overflow and also warnings on 64-bit builds
+ size_t strLen = s == NULL ? 0 : strlen(s);
- if (slen == 0)
+ if (strLen == 0)
return OK;
if (theStrBuf == NULL || theStrBuf->buf == NULL)
return NOTOK;
- if (theStrBuf->size + slen > theStrBuf->capacity)
+ if (theStrBuf->size + strLen > INT_MAX)
+ return NOTOK;
+
+ if ((size_t)theStrBuf->size + strLen > (size_t)theStrBuf->capacity)
{
- int newLen = theStrBuf->size + slen > 2 * theStrBuf->capacity ? theStrBuf->size + slen : 2 * theStrBuf->capacity;
- char *newBuf = (char *)malloc((newLen + 1) * sizeof(char));
+ size_t newLen = 0, doubleCapacity = 0;
+ char *newBuf = NULL;
+ doubleCapacity = ((size_t)theStrBuf->capacity) << 1;
+ newLen = theStrBuf->size + strLen;
+ if (newLen < doubleCapacity && (doubleCapacity + 1) <= INT_MAX)
+ newLen = doubleCapacity;
+
+ if ((newLen + 1) > INT_MAX)
+ return NOTOK;
+
+ 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;
+ theStrBuf->capacity = (int)newLen;
}
strcpy(theStrBuf->buf + theStrBuf->size, s);
- theStrBuf->size += slen;
+ theStrBuf->size += (int)strLen;
return OK;
}
@@ -224,6 +239,21 @@ int sb_ConcatChar(strBufP theStrBuf, char ch)
return sb_ConcatString(theStrBuf, s);
}
+/********************************************************************
+ sb_ConcatInt()
+ Converts theInt into a string, then invokes sb_ConcatString().
+ Returns Same as sb_ConcatString()
+ ********************************************************************/
+int sb_ConcatInt(strBufP theStrBuf, int theInt)
+{
+ char numberStr[MAXCHARSFOR32BITINT + 1];
+
+ if (sprintf(numberStr, "%d", theInt) < 1)
+ return NOTOK;
+
+ return sb_ConcatString(theStrBuf, numberStr);
+}
+
/********************************************************************
sb_TakeString()
Extracts the buffer string from the received string buffer and
diff --git a/planarity/c/graphLib/io/strbuf.h b/planarity/c/graphLib/io/strbuf.h
index 24c02bc..fe79914 100644
--- a/planarity/c/graphLib/io/strbuf.h
+++ b/planarity/c/graphLib/io/strbuf.h
@@ -23,12 +23,12 @@ extern "C"
typedef strBuf *strBufP;
- strBufP sb_New(int);
- void sb_Free(strBufP *);
+ strBufP sb_New(int capacity);
+ void sb_Free(strBufP *pStrBuf);
- void sb_ClearBuf(strBufP);
- int sb_Copy(strBufP, strBufP);
- strBufP sb_Duplicate(strBufP);
+ void sb_ClearBuf(strBufP theStrBuf);
+ int sb_Copy(strBufP strBufDst, strBufP strBufSrc);
+ strBufP sb_Duplicate(strBufP theStrBuf);
#define sb_GetFullString(theStrBuf) (theStrBuf->buf)
#define sb_GetSize(theStrBuf) (theStrBuf->size)
@@ -42,17 +42,18 @@ extern "C"
theStrBuf->readPos = theReadPos; \
}
- void sb_ReadSkipWhitespace(strBufP);
- void sb_ReadSkipInteger(strBufP);
+ void sb_ReadSkipWhitespace(strBufP theStrBuf);
+ void sb_ReadSkipInteger(strBufP theStrBuf);
#define sb_ReadSkipChar(theStrBuf) \
{ \
theStrBuf->readPos++; \
}
- int sb_ConcatString(strBufP, char const *);
- int sb_ConcatChar(strBufP, char);
+ int sb_ConcatString(strBufP theStrBuf, char const *s);
+ int sb_ConcatChar(strBufP theStrBuf, char ch);
+ int sb_ConcatInt(strBufP theStrBuf, int value);
- char *sb_TakeString(strBufP);
+ char *sb_TakeString(strBufP theStrBuf);
#ifndef SPEED_MACROS
// Optimized SPEED_MACROS versions of larger methods are not used in this module
diff --git a/planarity/c/graphLib/lowLevelUtils/apiutils.c b/planarity/c/graphLib/lowLevelUtils/apiutils.c
index 9519eee..df167b2 100644
--- a/planarity/c/graphLib/lowLevelUtils/apiutils.c
+++ b/planarity/c/graphLib/lowLevelUtils/apiutils.c
@@ -80,6 +80,33 @@ void gp_LogErrorMessage(int lineNum, const char *srcFileName, const char *messag
}
}
+/********************************************************************
+ gp_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 gp_GetRandomNumber(int NMin, int NMax)
+{
+ unsigned int N = (unsigned int)rand();
+
+ if (NMax < NMin)
+ return NMin;
+
+ /* Folded in unsigned arithmetic because the additions below overflow a
+ signed int for rand() results just below INT_MAX. */
+
+ N += ((N & 0xFFFF0000) >> 16);
+ N += ((N & 0x0000FF00) >> 8);
+ N %= (unsigned int)(NMax - NMin + 1);
+ return (int)(N & 0x7FFFFFFF) + NMin;
+}
+
+
/********************************************************************
debugNOTOK()
diff --git a/planarity/c/graphLib/lowLevelUtils/apiutils.h b/planarity/c/graphLib/lowLevelUtils/apiutils.h
index b2a33f9..314fc03 100644
--- a/planarity/c/graphLib/lowLevelUtils/apiutils.h
+++ b/planarity/c/graphLib/lowLevelUtils/apiutils.h
@@ -11,7 +11,8 @@ extern "C"
{
#endif
-#include "stdio.h"
+#include
+#include
#define MAXLINE 1024
@@ -55,6 +56,8 @@ extern "C"
#define gp_ErrorMessage(...) (gp_LogErrorMessage(__LINE__, __FILE__, __VA_ARGS__))
void gp_LogErrorMessage(int lineNum, const char *srcFileName, const char *message, ...) FORMAT_PRINTF(3, 4);
+ int gp_GetRandomNumber(int NMin, int NMax);
+
#ifdef __cplusplus
}
#endif
diff --git a/planarity/c/graphLib/lowLevelUtils/apiutils.private.h b/planarity/c/graphLib/lowLevelUtils/apiutils.private.h
index c057bb6..f1734de 100644
--- a/planarity/c/graphLib/lowLevelUtils/apiutils.private.h
+++ b/planarity/c/graphLib/lowLevelUtils/apiutils.private.h
@@ -35,11 +35,11 @@ extern "C"
#define _gp_MakeLogStr4 _MakeLogStr4
#define _gp_MakeLogStr5 _MakeLogStr5
- char *_MakeLogStr1(const char *format, int);
- char *_MakeLogStr2(const char *format, int, int);
- char *_MakeLogStr3(const char *format, int, int, int);
- char *_MakeLogStr4(const char *format, int, int, int, int);
- char *_MakeLogStr5(const char *format, int, int, int, int, int);
+ char *_MakeLogStr1(const char *format, int one);
+ char *_MakeLogStr2(const char *format, int one, int two);
+ char *_MakeLogStr3(const char *format, int one, int two, int three);
+ char *_MakeLogStr4(const char *format, int one, int two, int three, int four);
+ char *_MakeLogStr5(const char *format, int one, int two, int three, int four, int five);
#else
#define _gp_LogLine(Line)
diff --git a/planarity/c/graphLib/lowLevelUtils/appconst.h b/planarity/c/graphLib/lowLevelUtils/appconst.h
index 3038376..18521e1 100644
--- a/planarity/c/graphLib/lowLevelUtils/appconst.h
+++ b/planarity/c/graphLib/lowLevelUtils/appconst.h
@@ -50,7 +50,7 @@ See the LICENSE.TXT file for licensing information.
#undef NOTOK
extern int debugNOTOK(void);
#include
-#define NOTOK (printf("NOTOK on Line %d of %s\n", __LINE__, __FILE__), debugNOTOK())
+#define NOTOK (gp_ErrorMessage("NOTOK occurred"), debugNOTOK())
#endif
#ifndef TRUE
@@ -79,7 +79,7 @@ extern int debugNOTOK(void);
#undef USE_1BASEDARRAYS
#endif
-/* Array indices are used as pointers, and NIL means bad pointer */
+/* Array indexes are used as pointers, and NIL means bad pointer */
#ifdef USE_1BASEDARRAYS
// This definition is used with 1-based array indexing
#define NIL 0
diff --git a/planarity/c/graphLib/lowLevelUtils/stack.h b/planarity/c/graphLib/lowLevelUtils/stack.h
index 2c772fc..25bff88 100644
--- a/planarity/c/graphLib/lowLevelUtils/stack.h
+++ b/planarity/c/graphLib/lowLevelUtils/stack.h
@@ -24,10 +24,10 @@ extern "C"
typedef struct stackStruct stackStruct;
typedef stackStruct *stackP;
- stackP sp_New(int);
- void sp_Free(stackP *);
+ stackP sp_New(int capacity);
+ void sp_Free(stackP *pStack);
- int sp_Copy(stackP, stackP);
+ int sp_Copy(stackP stackDst, stackP stackSrc);
int sp_CopyContent(stackP stackDst, stackP stackSrc);
stackP sp_Duplicate(stackP theStack);
@@ -36,12 +36,12 @@ extern "C"
#ifndef SPEED_MACROS
- int sp_ClearStack(stackP);
+ int sp_ClearStack(stackP theStack);
int sp_GetCurrentSize(stackP theStack);
int sp_SetCurrentSize(stackP theStack, int top);
- int sp_IsEmpty(stackP);
- int sp_NonEmpty(stackP);
+ int sp_IsEmpty(stackP theStack);
+ int sp_NonEmpty(stackP theStack);
#define sp_Push(theStack, a) \
{ \
@@ -54,8 +54,8 @@ extern "C"
return NOTOK; \
}
- int sp__Push(stackP, int);
- int sp__Push2(stackP, int, int);
+ int sp__Push(stackP theStack, int a);
+ int sp__Push2(stackP theStack, int a, int b);
#define sp_Pop(theStack, a) \
{ \
@@ -84,16 +84,16 @@ extern "C"
return NOTOK; \
}
- int sp__Pop(stackP, int *);
+ int sp__Pop(stackP theStack, int *pA);
int sp__Pop_Discard(stackP theStack);
- int sp__Pop2(stackP, int *, int *);
+ int sp__Pop2(stackP theStack, int *pA, int *pB);
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);
+ int sp_Top(stackP theStack);
+ int sp_Get(stackP theStack, int pos);
+ int sp_Set(stackP theStack, int pos, int val);
#else
diff --git a/planarity/c/graphLib/planarityRelated/graphDrawPlanar.c b/planarity/c/graphLib/planarityRelated/graphDrawPlanar.c
index 4a83757..79917a3 100644
--- a/planarity/c/graphLib/planarityRelated/graphDrawPlanar.c
+++ b/planarity/c/graphLib/planarityRelated/graphDrawPlanar.c
@@ -91,7 +91,7 @@ int _ComputeVertexPositions(DrawPlanarContext *context)
{
// For each DFS tree root in the embedding, we
// compute the vertex positions
- if (gp_IsDFSTreeRoot(theEmbedding, v))
+ if (_gp_IsDFSTreeRoot(theEmbedding, v))
{
if (_ComputeVertexPositionsInComponent(context, v, &vertpos) != OK)
return NOTOK;
@@ -329,7 +329,7 @@ void _LogEdgeList(graphP theEmbedding, listCollectionP edgeList, int edgeListHea
advances through the vertices according to their assigned
vertical positions.
- The 'visitedInfo' member of each vertex is used to indicate the
+ The 'visitedIndex' 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
@@ -379,7 +379,7 @@ int _ComputeEdgePositions(DrawPlanarContext *context)
// Each vertex starts out with a NIL generator edge.
for (v = gp_LowerBoundVertices(theEmbedding); v < gp_UpperBoundVertices(theEmbedding); ++v)
- gp_SetVertexVisitedInfo(theEmbedding, v, NIL);
+ gp_SetVertexVisitedIndex(theEmbedding, v, NIL);
// Perform the vertical sweep of the combinatorial embedding, using
// the vertex ordering to guide the sweep.
@@ -399,13 +399,13 @@ int _ComputeEdgePositions(DrawPlanarContext *context)
// 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))
+ 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);
+ gp_SetVertexVisitedIndex(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
@@ -419,7 +419,7 @@ int _ComputeEdgePositions(DrawPlanarContext *context)
gp_GetIndex(theEmbedding, v), gp_GetIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e))));
// Set the generator edge for the root's neighbor
- gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, e), e);
+ gp_SetVertexVisitedIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e), e);
// Go to the next node of the root's adj list
e = gp_GetNextEdge(theEmbedding, e);
@@ -431,7 +431,7 @@ int _ComputeEdgePositions(DrawPlanarContext *context)
{
// 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);
+ eTwin = gp_GetVertexVisitedIndex(theEmbedding, v);
if (gp_IsNotEdge(theEmbedding, eTwin))
return NOTOK;
e = gp_GetTwin(theEmbedding, eTwin);
@@ -465,9 +465,9 @@ int _ComputeEdgePositions(DrawPlanarContext *context)
// 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_IsNotEdge(theEmbedding, gp_GetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur))))
+ if (gp_IsNotEdge(theEmbedding, gp_GetVertexVisitedIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur))))
{
- gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur), eCur);
+ gp_SetVertexVisitedIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur), eCur);
_gp_LogLine(_gp_MakeLogStr2("Generator edge (%d, %d)",
gp_GetIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwin(theEmbedding, e))),
gp_GetIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur))));
@@ -646,6 +646,13 @@ void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int
graphP theEmbedding = context->theGraph;
int K, Parent, BicompRoot, DFSChild, direction, descendant;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)RootVertex;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)W;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)WPrevLink;
+
_gp_LogLine("\ngraphDrawPlanar.c/_CollectDrawingData() start");
_gp_LogLine(_gp_MakeLogStr3("_CollectDrawingData(RootVertex=%d, W=%d, W_in=%d)",
RootVertex, W, WPrevLink));
@@ -753,7 +760,7 @@ int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink)
or 'beyond' its parent relative to what. */
context->VI[DFSChild].ancestorChild = gp_GetDFSChildFromBicompRoot(theEmbedding, BicompRoot);
- context->VI[DFSChild].ancestor = gp_GetVertexFromBicompRoot(theEmbedding, BicompRoot);
+ context->VI[DFSChild].ancestor = _gp_GetVertexFromBicompRoot(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));
@@ -1242,4 +1249,3 @@ int gp_DrawPlanar_GetEdgeEnd(graphP theEmbedding, int e)
return context->E[e].end;
}
-
diff --git a/planarity/c/graphLib/planarityRelated/graphDrawPlanar.private.h b/planarity/c/graphLib/planarityRelated/graphDrawPlanar.private.h
index 39de516..4784e3c 100644
--- a/planarity/c/graphLib/planarityRelated/graphDrawPlanar.private.h
+++ b/planarity/c/graphLib/planarityRelated/graphDrawPlanar.private.h
@@ -70,6 +70,9 @@ extern "C"
// Helps distinguish initialize from re-initialize
int initialized;
+ // Indicates that the visibility representation data is valid
+ int drawingDataValid;
+
// The graph that this context augments
graphP theGraph;
diff --git a/planarity/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c b/planarity/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c
index 51ac7fb..284258d 100644
--- a/planarity/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c
+++ b/planarity/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c
@@ -12,10 +12,13 @@ See the LICENSE.TXT file for licensing information.
#include
-extern void _ClearVertexVisitedFlags(graphP theGraph, int);
+extern void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices);
extern void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink);
extern int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink);
+extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R);
+extern int _CheckKuratowskiSubgraphIntegrity(graphP theGraph);
+extern int _TestSubgraph(graphP theSubgraph, graphP theGraph);
extern int _ComputeVisibilityRepresentation(DrawPlanarContext *context);
extern int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context);
@@ -33,6 +36,7 @@ void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v);
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_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R);
int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult);
int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
@@ -49,7 +53,7 @@ int _DrawPlanar_WritePostprocess(graphP theGraph, char **pExtraData);
void *_DrawPlanar_DupContext(void *pContext, void *theGraph);
int _DrawPlanar_CopyData(void *dstContext, void *srcContext);
-void _DrawPlanar_FreeContext(void *);
+void _DrawPlanar_FreeContext(void *pContext);
/****************************************************************************
* DRAWPLANAR_ID - the variable used to hold the integer identifier for this
@@ -82,7 +86,7 @@ int gp_ExtendWith_DrawPlanar(graphP theGraph)
{
DrawPlanarContext *context = NULL;
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// If the drawing feature has already been attached to the graph,
@@ -106,6 +110,7 @@ int gp_ExtendWith_DrawPlanar(graphP theGraph)
// First, tell the context that it is not initialized
context->initialized = 0;
+ context->drawingDataValid = FALSE;
// Save a pointer to theGraph in the context
context->theGraph = theGraph;
@@ -117,6 +122,7 @@ int gp_ExtendWith_DrawPlanar(graphP theGraph)
context->functions.fpMergeBicomps = _DrawPlanar_MergeBicomps;
context->functions.fpHandleInactiveVertex = _DrawPlanar_HandleInactiveVertex;
+ context->functions.fpHandleBlockedBicomp = _DrawPlanar_HandleBlockedBicomp;
context->functions.fpEmbedPostprocess = _DrawPlanar_EmbedPostprocess;
context->functions.fpCheckEmbeddingIntegrity = _DrawPlanar_CheckEmbeddingIntegrity;
context->functions.fpCheckObstructionIntegrity = _DrawPlanar_CheckObstructionIntegrity;
@@ -134,8 +140,8 @@ int gp_ExtendWith_DrawPlanar(graphP theGraph)
// 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_CopyData,
+ _DrawPlanar_DupContext,
+ _DrawPlanar_CopyData,
_DrawPlanar_FreeContext,
&context->functions) != OK)
{
@@ -194,6 +200,8 @@ void _DrawPlanar_ClearStructures(DrawPlanarContext *context)
context->E = NULL;
context->VI = NULL;
+ context->drawingDataValid = FALSE;
+
context->initialized = 1;
}
else
@@ -208,6 +216,8 @@ void _DrawPlanar_ClearStructures(DrawPlanarContext *context)
free(context->VI);
context->VI = NULL;
}
+
+ context->drawingDataValid = FALSE;
}
}
@@ -246,7 +256,6 @@ int _DrawPlanar_InitStructures(DrawPlanarContext *context)
memset(context->VI, NIL_CHAR, gp_UpperBoundVertices(context->theGraph) * sizeof(DrawPlanar_VertexInfo));
#else
graphP theGraph = context->theGraph;
-
if (gp_GetN(theGraph) <= 0)
return NOTOK;
@@ -256,6 +265,8 @@ int _DrawPlanar_InitStructures(DrawPlanarContext *context)
memset(context->E, 0, gp_UpperBoundEdgeStorage(context->theGraph) * sizeof(DrawPlanar_EdgeRec));
+ context->drawingDataValid = FALSE;
+
return OK;
}
@@ -315,6 +326,8 @@ int _DrawPlanar_CopyData(void *dstContext, void *srcContext)
return _DrawPlanar_InitStructures(dstDrawPlanarContext);
// ELSE: If there is also a srcContext, then we copy data from it
+ dstDrawPlanarContext->drawingDataValid = srcDrawPlanarContext->drawingDataValid;
+
dstEdgeStorage = gp_UpperBoundEdgeStorage(dstDrawPlanarContext->theGraph);
srcEdgeStorage = gp_UpperBoundEdgeStorage(srcDrawPlanarContext->theGraph);
@@ -562,6 +575,37 @@ int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, i
return NOTOK;
}
+/********************************************************************
+ ********************************************************************/
+
+int _DrawPlanar_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R)
+{
+ DrawPlanarContext *context = NULL;
+ gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
+
+ if (context != NULL)
+ {
+ if (gp_GetEmbedFlags(theGraph) == EMBEDFLAGS_DRAWPLANAR)
+ {
+ int RetVal = NONEMBEDDABLE;
+
+ context->drawingDataValid = FALSE;
+
+ if (R != RootVertex)
+ sp_Push2(theGraph->theStack, R, 0);
+
+ if (_IsolateKuratowskiSubgraph(theGraph, v, RootVertex) != OK)
+ RetVal = NOTOK;
+
+ return RetVal;
+ }
+
+ return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R);
+ }
+
+ return NOTOK;
+}
+
/********************************************************************
********************************************************************/
@@ -602,9 +646,13 @@ int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult
if (gp_GetEmbedFlags(theGraph) == EMBEDFLAGS_DRAWPLANAR)
{
+ context->drawingDataValid = FALSE;
+
if (RetVal == OK)
{
RetVal = _ComputeVisibilityRepresentation(context);
+ if (RetVal == OK)
+ context->drawingDataValid = TRUE;
}
}
@@ -638,7 +686,23 @@ int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
{
- return OK;
+ DrawPlanarContext *context = NULL;
+ gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);
+
+ if (context != NULL)
+ {
+ if (gp_GetEmbedFlags(theGraph) == EMBEDFLAGS_DRAWPLANAR)
+ {
+ if (_TestSubgraph(theGraph, origGraph) != TRUE)
+ return NOTOK;
+
+ return _CheckKuratowskiSubgraphIntegrity(theGraph);
+ }
+
+ return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
+ }
+
+ return NOTOK;
}
/********************************************************************
@@ -690,6 +754,8 @@ int _DrawPlanar_ReadPostprocess(graphP theGraph, char *extraData)
extraData = strchr(extraData, '\n') + 1;
}
+
+ context->drawingDataValid = TRUE;
}
return OK;
@@ -725,10 +791,15 @@ int _DrawPlanar_WritePostprocess(graphP theGraph, char **pExtraData)
int v, e;
char line[64];
int maxLineSize = 64, extraDataPos = 0;
- char *extraData = (char *)calloc((1 + gp_GetN(theGraph) + 2 * gp_GetM(theGraph) + 1) * maxLineSize, sizeof(char));
+ char *extraData = NULL;
int zeroBasedVertexOffset = 0;
int zeroBasedEdgeOffset = 0;
+ if (!context->drawingDataValid)
+ return OK;
+
+ extraData = (char *)calloc((1 + gp_GetN(theGraph) + 2 * gp_GetM(theGraph) + 1) * maxLineSize, sizeof(char));
+
if (extraData == NULL)
return NOTOK;
diff --git a/planarity/c/graphLib/planarityRelated/graphEmbed.c b/planarity/c/graphLib/planarityRelated/graphEmbed.c
index 30f3e0f..98dd2b5 100644
--- a/planarity/c/graphLib/planarityRelated/graphEmbed.c
+++ b/planarity/c/graphLib/planarityRelated/graphEmbed.c
@@ -23,7 +23,8 @@ See the LICENSE.TXT file for licensing information.
/* Imported functions */
-extern void _ClearVertexVisitedFlags(graphP theGraph, int);
+extern void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices);
+extern int _FillVertexVisitedIndexes(graphP theGraph, int FillValue);
extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R);
extern int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R);
@@ -36,6 +37,8 @@ extern int _gp_FindEdge(graphP theGraph, int u, int v);
int _gp_EmbedFlagsValid(graphP theGraph, int embedFlags);
int _EmbeddingInitialize(graphP theGraph);
+int _EmbeddingInitialize_Optimized(graphP theGraph);
+int _EmbeddingInitialize_Incremental(graphP theGraph);
void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
@@ -249,6 +252,275 @@ int _gp_EmbedFlagsValid(graphP theGraph, int embedFlags)
/********************************************************************
_EmbeddingInitialize()
+ Routes to the full initialization path when no DFS state is present,
+ otherwise uses the incremental path to honor DFS utility work already
+ performed by the caller.
+ ********************************************************************/
+int _EmbeddingInitialize(graphP theGraph)
+{
+ unsigned graphFlags;
+
+ if (theGraph == NULL)
+ return NOTOK;
+
+ graphFlags = gp_GetGraphFlags(theGraph);
+
+ if (!(graphFlags & (GRAPHFLAGS_DFSNUMBERED |
+ GRAPHFLAGS_SORTEDBYDFI |
+ GRAPHFLAGS_LOWPOINTSCOMPUTED)))
+ {
+ return _EmbeddingInitialize_Optimized(theGraph);
+ }
+
+ return _EmbeddingInitialize_Incremental(theGraph);
+}
+
+/********************************************************************
+ _EmbeddingInitialize_Incremental()
+
+ Uses previously computed DFS, sort, and/or lowpoint data to perform
+ only the embedding initialization steps still required by gp_Embed().
+ ********************************************************************/
+int _EmbeddingInitialize_Incremental(graphP theGraph)
+{
+ stackP theStack;
+ unsigned graphFlags;
+ int v, R, uparent, u, uneighbor, e, f, eTwin, ePrev, eNext;
+
+ _gp_LogLine("graphEmbed.c/_EmbeddingInitialize_Incremental() start\n");
+
+ graphFlags = gp_GetGraphFlags(theGraph);
+
+ if ((graphFlags & GRAPHFLAGS_SORTEDBYDFI) &&
+ !(graphFlags & GRAPHFLAGS_DFSNUMBERED))
+ {
+ gp_ErrorMessage("Invalid graph flags: SORTEDBYDFI requires DFSNUMBERED.");
+ return NOTOK;
+ }
+
+ if ((graphFlags & GRAPHFLAGS_LOWPOINTSCOMPUTED) &&
+ ((graphFlags & (GRAPHFLAGS_DFSNUMBERED | GRAPHFLAGS_SORTEDBYDFI)) !=
+ (GRAPHFLAGS_DFSNUMBERED | GRAPHFLAGS_SORTEDBYDFI)))
+ {
+ gp_ErrorMessage("Invalid graph flags: LOWPOINTSCOMPUTED requires DFSNUMBERED and SORTEDBYDFI.");
+ return NOTOK;
+ }
+
+ // Start with the standard initializations to perform depth-first search,
+ // sorting vertices (in linear time) by their depth-first indexes, and
+ // computing least ancestor and lowpoint values for the vertices, if they
+ // have not already been done before calling gp_Embed()
+ if (!(graphFlags & GRAPHFLAGS_DFSNUMBERED))
+ {
+ if (gp_DepthFirstSearch(theGraph) != OK)
+ return NOTOK;
+ graphFlags = gp_GetGraphFlags(theGraph);
+ }
+
+ if (!(graphFlags & GRAPHFLAGS_SORTEDBYDFI))
+ {
+ if (gp_SortVertices(theGraph) != OK)
+ return NOTOK;
+ graphFlags = gp_GetGraphFlags(theGraph);
+ }
+
+ if (!(graphFlags & GRAPHFLAGS_LOWPOINTSCOMPUTED))
+ {
+ if (gp_ComputeLowpoints(theGraph) != OK)
+ return NOTOK;
+ graphFlags = gp_GetGraphFlags(theGraph);
+ }
+
+ // The planarity embedder uses the visitedIndex like a flag, except that equality
+ // means 'set' and greater than means 'clear'. So, the 'flag' is implicitly
+ // cleared when the main embedding loop decrements v to process the next lower
+ // numbered vertex (because then all the visitedIndex values are greater than v).
+ // This call starts all 'flags' as clear since gp_UpperBoundVertices() returns
+ // a value one greater than the highest numbered vertex.
+ if (_FillVertexVisitedIndexes(theGraph, gp_UpperBoundVertices(theGraph)) != OK)
+ return NOTOK;
+
+ // Use the stack and visited flags to traverse the previously created DFS tree to
+ // do initializations that build up the sorted DFS child lists of each vertex, to
+ // associate each tree edge with the bicomp root associated with the child endpoint,
+ // and to remove the back edges from being embedded in the graph and instead put
+ // their forward edge records in the forward edge lists of the ancestor endpoints
+ // (so back edges from a vertex to its descendants can be easily processed).
+ theStack = theGraph->theStack;
+
+ if (sp_GetCapacity(theStack) < 2 * 2 * gp_GetM(theGraph) + 2)
+ return NOTOK;
+
+ sp_ClearStack(theStack);
+ _ClearVertexVisitedFlags(theGraph, FALSE);
+
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ {
+ if (gp_IsVertex(theGraph, gp_GetVertexParent(theGraph, v)))
+ continue;
+
+ sp_Push2(theStack, NIL, NIL);
+ while (sp_NonEmpty(theStack))
+ {
+ sp_Pop2(theStack, uparent, e);
+ u = gp_IsNotVertex(theGraph, uparent) ? v : gp_GetNeighbor(theGraph, e);
+
+ if (!gp_GetVisited(theGraph, u))
+ {
+ gp_SetVisited(theGraph, u);
+
+ if (gp_IsEdge(theGraph, e))
+ {
+ // If we are visiting a previously unvisited vertex via an existing edge,
+ // then of course we are arriving via a DFS tree edge from its parent.
+ if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_CHILD)
+ return NOTOK;
+
+ // Build up the sorted DFS child lists of each vertex (by appending
+ // the vertex being visited to the sorted DFS child list of its parent)
+ gp_SetVertexSortedDFSChildList(theGraph, uparent,
+ gp_AppendDFSChild(theGraph, uparent, u));
+
+ // Associate each tree edge with the bicomp root associated with the child endpoint
+ R = gp_GetBicompRootFromDFSChild(theGraph, u);
+ gp_SetFirstEdge(theGraph, R, e);
+ gp_SetLastEdge(theGraph, R, e);
+ }
+
+ // Iterate the adjacency list of vertex u
+ e = gp_GetFirstEdge(theGraph, u);
+ while (gp_IsEdge(theGraph, e))
+ {
+ if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
+ {
+ // If the edge record points to an unvisited DFS child, then
+ // that is a new vertex to add to the navigation stack
+ if (!gp_GetVisited(theGraph, gp_GetNeighbor(theGraph, e)))
+ sp_Push2(theStack, u, e);
+ }
+ else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK)
+ {
+ // For each back edge record, we get the associated forward
+ // edge record (into eTwin) and info about it.
+ eTwin = gp_GetTwin(theGraph, e);
+ uneighbor = gp_GetNeighbor(theGraph, e);
+ ePrev = gp_GetPrevEdge(theGraph, eTwin);
+ eNext = gp_GetNextEdge(theGraph, eTwin);
+
+ // The forward edge record is then removed from the adjacency list
+ // of the ancestor (uneighbor), and...
+ if (gp_IsEdge(theGraph, ePrev))
+ gp_SetNextEdge(theGraph, ePrev, eNext);
+ else
+ gp_SetFirstEdge(theGraph, uneighbor, eNext);
+ if (gp_IsEdge(theGraph, eNext))
+ gp_SetPrevEdge(theGraph, eNext, ePrev);
+ else
+ gp_SetLastEdge(theGraph, uneighbor, ePrev);
+
+ // ... placed into the forward edge list of the ancestor (uneighbor)
+ if (gp_IsEdge(theGraph, f = gp_GetVertexFwdEdgeList(theGraph, uneighbor)))
+ {
+ ePrev = gp_GetPrevEdge(theGraph, f);
+ gp_SetPrevEdge(theGraph, eTwin, ePrev);
+ gp_SetNextEdge(theGraph, eTwin, f);
+ gp_SetPrevEdge(theGraph, f, eTwin);
+ gp_SetNextEdge(theGraph, ePrev, eTwin);
+ }
+ else
+ {
+ gp_SetVertexFwdEdgeList(theGraph, uneighbor, eTwin);
+ gp_SetPrevEdge(theGraph, eTwin, eTwin);
+ gp_SetNextEdge(theGraph, eTwin, eTwin);
+ }
+ }
+
+ e = gp_GetNextEdge(theGraph, e);
+ }
+ }
+ }
+ }
+
+ // Initialize the future pertinent child of each vertex to just be the first
+ // element of the sorted DFS child list. Initially, the embedding comprises
+ // only DFS tree edges embedded as singleton bicomps, so every DFS child is
+ // separated from its DFS parent(in a separate bicomp from its parent). In the
+ // first articulation of the edge addition planarity algorithm, the knowledge
+ // of future pertinent children was managed by a "separated DFS child list" that
+ // was sorted by lowpoint. In the current implementation, this has been
+ // relaxed. We start out with any separated DFS child, but then just before
+ // the future pertinent child must be used in a vertex step v, it is updated to
+ // be the first child that has any lowpoint less than v (it doesn't have to be
+ // the lowest, so we don't need a separated DFS child list strictly sorted by
+ // lowpoint). See the invocations of gp_UpdateVertexFuturePertinentChild().
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ gp_SetVertexFuturePertinentChild(theGraph, v, gp_GetVertexSortedDFSChildList(theGraph, v));
+
+ // A second pass through the vertices to set up the embedding of all
+ // DFS tree edges as singleton comps
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ {
+ if (_gp_IsDFSTreeRoot(theGraph, v))
+ {
+ // Each DFS tree root vertex is not going to be in a singleton bicomp
+ // with its DFS parent because it has no parent, so it will initially
+ // have no adjacency list entries (rather than having an adjacency list
+ // entry for a tree edge to its parent, as in the else clause).
+ gp_SetFirstEdge(theGraph, v, NIL);
+ gp_SetLastEdge(theGraph, v, NIL);
+ }
+ else
+ {
+ // For each vertex v that is not a DFS tree root, v will be joining
+ // a virtual vertex R representing its DFS parent in an embedding of
+ // a singleton biconnected component containing only the DFS tree edge
+ // between v and the virtual vertex R representing v's DFS parent.
+ // In the preceding initialization for-loop, the DFS tree edge was
+ // already linked to R, so we obtain R now...
+ R = gp_GetBicompRootFromDFSChild(theGraph, v);
+
+ // ... and then ensure that the DFS tree edge record is alone in the
+ // adjacency list of R...
+ e = gp_GetFirstEdge(theGraph, R);
+ gp_SetPrevEdge(theGraph, e, NIL);
+ gp_SetNextEdge(theGraph, e, NIL);
+
+ // ... then we get the twin edge record that will be going into
+ // v's adjacency list, and we reset its neighbor from indicating
+ // v's DFS parent to indicating R (because R is the virtual vertex
+ // representing v's parent in the bicomp that will contain v).
+ eTwin = gp_GetTwin(theGraph, e);
+ gp_SetNeighbor(theGraph, eTwin, R);
+
+ // Now we place that twin edge record into v's adjacency list as
+ // its only adjacency.
+ gp_SetFirstEdge(theGraph, v, eTwin);
+ gp_SetLastEdge(theGraph, v, eTwin);
+ gp_SetPrevEdge(theGraph, eTwin, NIL);
+ gp_SetNextEdge(theGraph, eTwin, NIL);
+
+ // And finally we initialize the auxiliary data structure that
+ // optimizes knowledge and management of the vertices on the
+ // external face of the bicomp rooted by R. At this initial stage,
+ // the external face contains only R and v. The links are arranged
+ // so that one can navigate around the external face from R to v a
+ // and back to R in either a clockwise or counterclockwise direction
+ // by consistently following either 0 links or 1 links.
+ 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_Incremental() end\n");
+
+ return OK;
+}
+
+/********************************************************************
+ _EmbeddingInitialize_Optimized()
+
This method performs the following tasks:
(1) Assign depth first index (DFI) and DFS parentvalues to vertices
(2) Assign DFS edge types
@@ -263,14 +535,17 @@ int _gp_EmbedFlagsValid(graphP theGraph, int embedFlags)
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.
+
+ This performs the same function as _EmbeddingInitialize_Incremental()
+ but has optimized total gp_Embed() execution by about 12%.
********************************************************************/
-int _EmbeddingInitialize(graphP theGraph)
+int _EmbeddingInitialize_Optimized(graphP theGraph)
{
stackP theStack;
int DFI, v, R, uparent, u, uneighbor, e, f, eTwin, ePrev, eNext;
int leastValue, child;
- _gp_LogLine("graphEmbed.c/_EmbeddingInitialize() start\n");
+ _gp_LogLine("graphEmbed.c/_EmbeddingInitialize_Optimized() start\n");
theStack = theGraph->theStack;
@@ -402,6 +677,13 @@ int _EmbeddingInitialize(graphP theGraph)
// The graph is now DFS numbered
theGraph->graphFlags |= GRAPHFLAGS_DFSNUMBERED;
+ if (gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DFSNUMBERED_DIRECTED)
+ {
+ theGraph->graphFlags &= ~GRAPHFLAGS_DFSNUMBERED_DIRECTED;
+ if (_FillVertexVisitedIndexes(theGraph, 0) != OK)
+ return NOTOK;
+ }
+
// (6) Now that all vertices have a DFI in the index member, we can sort vertices
if (gp_SortVertices(theGraph) != OK)
return NOTOK;
@@ -410,7 +692,7 @@ int _EmbeddingInitialize(graphP theGraph)
for (v = gp_UpperBoundVertices(theGraph) - 1; v >= gp_LowerBoundVertices(theGraph); --v)
{
// (7) Initialize for pertinence management
- gp_SetVertexVisitedInfo(theGraph, v, gp_GetN(theGraph));
+ gp_SetVertexVisitedIndex(theGraph, v, gp_UpperBoundVertices(theGraph));
// (7) Initialize for future pertinence management
child = gp_GetVertexSortedDFSChildList(theGraph, v);
@@ -428,7 +710,7 @@ int _EmbeddingInitialize(graphP theGraph)
// (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))
+ if (_gp_IsDFSTreeRoot(theGraph, v))
{
gp_SetFirstEdge(theGraph, v, NIL);
gp_SetLastEdge(theGraph, v, NIL);
@@ -460,7 +742,9 @@ int _EmbeddingInitialize(graphP theGraph)
}
}
- _gp_LogLine("graphEmbed.c/_EmbeddingInitialize() end\n");
+ theGraph->graphFlags |= GRAPHFLAGS_LOWPOINTSCOMPUTED;
+
+ _gp_LogLine("graphEmbed.c/_EmbeddingInitialize_Optimized() end\n");
return OK;
}
@@ -486,7 +770,7 @@ void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, i
/* The forward edge record is removed from the fwdEdgeList of the root's parent copy. */
- parentCopy = gp_GetVertexFromBicompRoot(theGraph, RootVertex);
+ parentCopy = _gp_GetVertexFromBicompRoot(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));
@@ -658,6 +942,15 @@ int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink)
{
int R, Rout, Z, ZPrevLink, e, extFaceVertex;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)v;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)RootVertex;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)W;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)WPrevLink;
+
while (sp_NonEmpty(theGraph->theStack))
{
sp_Pop2(theGraph->theStack, R, Rout);
@@ -719,7 +1012,9 @@ int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink)
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
+ // then we advance to the next child (or NIL) because future pertinence is based on
+ // back edge connections from vertices in other bicomps (i.e., DFS subtrees rooted by
+ // other DFS children of Z that have not yet been merged into the bicomp with Z).
if (gp_GetDFSChildFromBicompRoot(theGraph, R) == gp_GetVertexFuturePertinentChild(theGraph, Z))
{
gp_SetVertexFuturePertinentChild(theGraph, Z,
@@ -789,7 +1084,7 @@ void _WalkUp(graphP theGraph, int v, int e)
{
// 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)
+ if (gp_GetVertexVisitedIndex(theGraph, Zig) == v)
break;
// Store the bicomp root that was found
@@ -802,28 +1097,28 @@ void _WalkUp(graphP theGraph, int v, int e)
// 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)
+ if (gp_GetVertexVisitedIndex(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)
+ if (gp_GetVertexVisitedIndex(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)
+ if (gp_GetVertexVisitedIndex(theGraph, nextZig) == v)
break;
}
// The bicomp root was not found in either direction.
else
{
- if (gp_GetVertexVisitedInfo(theGraph, Zig) == v)
+ if (gp_GetVertexVisitedIndex(theGraph, Zig) == v)
break;
- if (gp_GetVertexVisitedInfo(theGraph, Zag) == v)
+ if (gp_GetVertexVisitedIndex(theGraph, Zag) == v)
break;
R = NIL;
}
@@ -831,8 +1126,8 @@ void _WalkUp(graphP theGraph, int v, int e)
// 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);
+ gp_SetVertexVisitedIndex(theGraph, Zig, v);
+ gp_SetVertexVisitedIndex(theGraph, Zag, v);
// If both directions found new non-root vertices, then proceed with parallel external face traversal
if (gp_IsNotVirtualVertex(theGraph, R))
@@ -851,7 +1146,7 @@ void _WalkUp(graphP theGraph, int v, int e)
// Step up from the bicomp root vertex (virtual) to the parent copy of
// the vertex (non-virtual; called parent copy because it is the one that
// is in a bicomp with a virtual or non-virtual copy of its DFS parent)
- Zig = Zag = gp_GetVertexFromBicompRoot(theGraph, R);
+ Zig = Zag = _gp_GetVertexFromBicompRoot(theGraph, R);
ZigPrevLink = 1;
ZagPrevLink = 0;
@@ -1237,7 +1532,12 @@ void _AdvanceFwdEdgeList(graphP theGraph, int v, int child, int nextChild)
int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink)
{
- int X = gp_GetExtFaceVertex(theGraph, *pW, 1 ^ *pWPrevLink);
+ int X;
+
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)BicompRoot;
+
+ X = gp_GetExtFaceVertex(theGraph, *pW, 1 ^ *pWPrevLink);
*pWPrevLink = gp_GetExtFaceVertex(theGraph, X, 0) == *pW ? 0 : 1;
*pW = X;
@@ -1273,6 +1573,9 @@ int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult)
{
int RetVal = edgeEmbeddingResult;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)v;
+
// 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)
@@ -1397,7 +1700,7 @@ int _JoinBicomps(graphP theGraph)
// If the bicomp root is still active (i.e. an in-use virtual vertex)
// then merge it with its parent copy vertex (non-virtual)
if (gp_VirtualVertexInUse(theGraph, R))
- _MergeVertex(theGraph, gp_GetVertexFromBicompRoot(theGraph, R), 0, R);
+ _MergeVertex(theGraph, _gp_GetVertexFromBicompRoot(theGraph, R), 0, R);
}
return OK;
@@ -1419,6 +1722,9 @@ int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x)
{
int e_u, e_v, e_ulink, e_vlink;
+ // Suppresses an unused-parameter warning for a parameter we intend to keep
+ (void)w;
+
// Get the edge record in u that indicates v; uses the "get twin" method
// to ensure the cost is dominated by the degree of v (which is 2), not u
// (which can be any degree).
diff --git a/planarity/c/graphLib/planarityRelated/graphIsolator.c b/planarity/c/graphLib/planarityRelated/graphIsolator.c
index f69cf0d..487b7c8 100644
--- a/planarity/c/graphLib/planarityRelated/graphIsolator.c
+++ b/planarity/c/graphLib/planarityRelated/graphIsolator.c
@@ -12,7 +12,7 @@ See the LICENSE.TXT file for licensing information.
/* Imported functions */
-extern void _ClearAllVisitedFlagsInGraph(graphP);
+extern void _ClearAllVisitedFlagsInGraph(graphP theGraph);
extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink);
extern int _JoinBicomps(graphP theGraph);
@@ -619,7 +619,7 @@ int _MarkDFSPath(graphP theGraph, int ancestor, int descendant)
// If we are marking from a root (virtual) vertex upward, then go up to the
// non-virtual parent copy before starting the loop
if (gp_IsVirtualVertex(theGraph, descendant))
- descendant = gp_GetVertexFromBicompRoot(theGraph, descendant);
+ descendant = _gp_GetVertexFromBicompRoot(theGraph, descendant);
// Mark the lowest vertex (the one with the highest number).
gp_SetVisited(theGraph, descendant);
@@ -637,7 +637,7 @@ int _MarkDFSPath(graphP theGraph, int ancestor, int descendant)
// counterpart, so that can also be marked as visited.
if (gp_IsVirtualVertex(theGraph, descendant))
{
- parent = gp_GetVertexFromBicompRoot(theGraph, descendant);
+ parent = _gp_GetVertexFromBicompRoot(theGraph, descendant);
}
// If we are on a regular, non-virtual vertex then get the edge to the parent,
diff --git a/planarity/c/graphLib/planarityRelated/graphNonplanar.c b/planarity/c/graphLib/planarityRelated/graphNonplanar.c
index 0414db5..db13ddb 100644
--- a/planarity/c/graphLib/planarityRelated/graphNonplanar.c
+++ b/planarity/c/graphLib/planarityRelated/graphNonplanar.c
@@ -71,7 +71,7 @@ int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R)
then the Walkdown terminated because it couldn't find
a viable path along a child bicomp, which is Minor A. */
- if (gp_GetVertexFromBicompRoot(theGraph, R) != v)
+ if (_gp_GetVertexFromBicompRoot(theGraph, R) != v)
{
theGraphIC(theGraph)->minorType |= MINORTYPE_A;
return OK;
@@ -202,7 +202,7 @@ int _InitializeNonplanarityContext(graphP theGraph, int v, int R)
// NOTE: We do not need to set up for xy path identification when we
// have minor A, which occurs when the parent copy vertex of
// bicomp root R is a descendant of v (not v).
- if (gp_GetVertexFromBicompRoot(theGraph, R) == v)
+ if (_gp_GetVertexFromBicompRoot(theGraph, R) == v)
{
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
return NOTOK;
diff --git a/planarity/c/graphLib/planarityRelated/graphOuterplanarObstruction.c b/planarity/c/graphLib/planarityRelated/graphOuterplanarObstruction.c
index 50d331f..ed8b24d 100644
--- a/planarity/c/graphLib/planarityRelated/graphOuterplanarObstruction.c
+++ b/planarity/c/graphLib/planarityRelated/graphOuterplanarObstruction.c
@@ -9,7 +9,7 @@ See the LICENSE.TXT file for licensing information.
/* Imported functions */
-extern void _ClearAllVisitedFlagsInGraph(graphP);
+extern void _ClearAllVisitedFlagsInGraph(graphP theGraph);
extern int _JoinBicomps(graphP theGraph);
@@ -59,7 +59,7 @@ int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R)
// 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_GetVertexFromBicompRoot(theGraph, R) != v)
+ if (_gp_GetVertexFromBicompRoot(theGraph, R) != v)
{
theGraphIC(theGraph)->minorType |= MINORTYPE_A;
return OK;
diff --git a/planarity/c/graphLib/planarityRelated/graphOuterplanarity_Extensions.c b/planarity/c/graphLib/planarityRelated/graphOuterplanarity_Extensions.c
index c2e640c..ff5874c 100644
--- a/planarity/c/graphLib/planarityRelated/graphOuterplanarity_Extensions.c
+++ b/planarity/c/graphLib/planarityRelated/graphOuterplanarity_Extensions.c
@@ -24,7 +24,7 @@ See the LICENSE.TXT file for licensing information.
int gp_ExtendWith_Outerplanarity(graphP theGraph)
{
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// If the Graph has already been extended with Outerplanarity,
diff --git a/planarity/c/graphLib/planarityRelated/graphPlanarity.h b/planarity/c/graphLib/planarityRelated/graphPlanarity.h
index 7c0fb03..4033dba 100644
--- a/planarity/c/graphLib/planarityRelated/graphPlanarity.h
+++ b/planarity/c/graphLib/planarityRelated/graphPlanarity.h
@@ -34,6 +34,10 @@ extern "C"
int gp_Embed(graphP theGraph, unsigned embedFlags);
int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult);
+ // Graph embedding face enumeration and listing methods
+ int gp_CountEmbeddingFaces(graphP theGraph);
+ int gp_CreateEmbeddingFaceList(graphP theGraph, char **pFaceList);
+
// A return result value for gp_Embed() to indicate success prior to embedding completion,
// due to finding an obstruction to embedding.
#define NONEMBEDDABLE -1
diff --git a/planarity/c/graphLib/planarityRelated/graphPlanarity.private.h b/planarity/c/graphLib/planarityRelated/graphPlanarity.private.h
index e7b39b5..5b22af9 100644
--- a/planarity/c/graphLib/planarityRelated/graphPlanarity.private.h
+++ b/planarity/c/graphLib/planarityRelated/graphPlanarity.private.h
@@ -74,11 +74,6 @@ extern "C"
//
Planarity-specific additional vertex information.
- 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
@@ -113,8 +108,6 @@ extern "C"
struct Planarity_VertexInfo
{
- int visitedInfo;
-
int pertinentEdge,
pertinentRoots,
futurePertinentChild,
@@ -125,9 +118,6 @@ extern "C"
typedef struct Planarity_VertexInfo Planarity_VertexInfo;
typedef Planarity_VertexInfo *Planarity_VertexInfoP;
-#define gp_GetVertexVisitedInfo(theGraph, v) (theGraphPVI(theGraph)[v].visitedInfo)
-#define gp_SetVertexVisitedInfo(theGraph, v, theVisitedInfo) (theGraphPVI(theGraph)[v].visitedInfo = theVisitedInfo)
-
#define gp_GetVertexPertinentEdge(theGraph, v) (theGraphPVI(theGraph)[v].pertinentEdge)
#define gp_SetVertexPertinentEdge(theGraph, v, e) (theGraphPVI(theGraph)[v].pertinentEdge = e)
diff --git a/planarity/c/graphLib/planarityRelated/graphPlanarity_Extensions.c b/planarity/c/graphLib/planarityRelated/graphPlanarity_Extensions.c
index a723c3d..f1fedfb 100644
--- a/planarity/c/graphLib/planarityRelated/graphPlanarity_Extensions.c
+++ b/planarity/c/graphLib/planarityRelated/graphPlanarity_Extensions.c
@@ -24,7 +24,7 @@ See the LICENSE.TXT file for licensing information.
int gp_ExtendWith_Planarity(graphP theGraph)
{
- if (theGraph == NULL || gp_GetN(theGraph) <= 0)
+ if (theGraph == NULL)
return NOTOK;
// If the Graph has already been extended with Planarity,
diff --git a/planarity/c/graphLib/planarityRelated/graphPlanarity_Faces.c b/planarity/c/graphLib/planarityRelated/graphPlanarity_Faces.c
new file mode 100644
index 0000000..412033b
--- /dev/null
+++ b/planarity/c/graphLib/planarityRelated/graphPlanarity_Faces.c
@@ -0,0 +1,280 @@
+/*
+Copyright (c) 1997-2026, John M. Boyer
+All rights reserved.
+See the LICENSE.TXT file for licensing information.
+*/
+
+#include "graphPlanarity.h"
+#include "graphPlanarity.private.h"
+#include "../io/strbuf.h"
+
+#include
+
+static int _ClearEmbeddingFaceEdgeVisitedFlags(graphP theGraph)
+{
+ int e, eTwin;
+
+ if (theGraph == NULL)
+ return NOTOK;
+
+ for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); e += 2)
+ {
+ if (gp_EdgeInUse(theGraph, e))
+ {
+ gp_ClearEdgeVisited(theGraph, e);
+ eTwin = gp_GetTwin(theGraph, e);
+ gp_ClearEdgeVisited(theGraph, eTwin);
+ }
+ }
+
+ return OK;
+}
+
+static int _AppendEmbeddingFaceListHeader(strBufP faceList, int componentNumber)
+{
+ return sb_ConcatString(faceList, "Component_") == OK &&
+ sb_ConcatInt(faceList, componentNumber) == OK &&
+ sb_ConcatString(faceList, ": |\n") == OK
+ ? OK
+ : NOTOK;
+}
+
+static int _AppendEmbeddingFace(strBufP faceList, graphP theGraph, int eStart)
+{
+ int e = eStart, eNext, faceVertex, firstVertex = NIL, firstInFace = TRUE;
+
+ if (sb_ConcatString(faceList, " ") != OK)
+ return NOTOK;
+
+ do
+ {
+ faceVertex = gp_GetNeighbor(theGraph, gp_GetTwin(theGraph, e));
+
+ if (firstInFace)
+ {
+ firstVertex = faceVertex;
+ firstInFace = FALSE;
+ }
+ else if (sb_ConcatString(faceList, ", ") != OK)
+ return NOTOK;
+
+ if (sb_ConcatInt(faceList, faceVertex) != OK)
+ return NOTOK;
+
+ eNext = gp_GetNextEdgeCircular(theGraph, gp_GetTwin(theGraph, e));
+ if (gp_GetEdgeVisited(theGraph, eNext))
+ return NOTOK;
+
+ gp_SetEdgeVisited(theGraph, eNext);
+ e = eNext;
+ } while (e != eStart);
+
+ return sb_ConcatString(faceList, ", ") == OK &&
+ sb_ConcatInt(faceList, firstVertex) == OK &&
+ sb_ConcatChar(faceList, '\n') == OK
+ ? OK
+ : NOTOK;
+}
+
+/********************************************************************
+ gp_CountEmbeddingFaces()
+
+ Traverses all faces of a graph structure containing a planar embedding
+ and returns the number of faces if the traversal succeeds and the face
+ count agrees with the extended Euler formula. Returns -1 on error.
+ ********************************************************************/
+
+int gp_CountEmbeddingFaces(graphP theGraph)
+{
+ stackP theStack;
+ int v, e, eTwin, eStart, eNext, NumFaces, connectedComponents;
+
+ if (theGraph == NULL || !(gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DFSNUMBERED))
+ return -1;
+
+ theStack = theGraph->theStack;
+ if (theStack == NULL || sp_GetCapacity(theStack) < 2 * gp_GetM(theGraph))
+ return -1;
+
+ sp_ClearStack(theStack);
+
+ for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); e += 2)
+ {
+ if (gp_EdgeInUse(theGraph, e))
+ {
+ sp_Push(theStack, e);
+ gp_ClearEdgeVisited(theGraph, e);
+
+ eTwin = gp_GetTwin(theGraph, e);
+ sp_Push(theStack, eTwin);
+ gp_ClearEdgeVisited(theGraph, eTwin);
+ }
+ }
+
+ if (sp_GetCurrentSize(theStack) != 2 * gp_GetM(theGraph))
+ return -1;
+
+ NumFaces = 0;
+ while (sp_NonEmpty(theStack))
+ {
+ sp_Pop(theStack, eStart);
+ if (gp_GetEdgeVisited(theGraph, eStart))
+ continue;
+
+ e = eStart;
+ do
+ {
+ eNext = gp_GetNextEdgeCircular(theGraph, gp_GetTwin(theGraph, e));
+ if (gp_GetEdgeVisited(theGraph, eNext))
+ return -1;
+
+ gp_SetEdgeVisited(theGraph, eNext);
+ e = eNext;
+ } while (e != eStart);
+
+ NumFaces++;
+ }
+
+ connectedComponents = 0;
+ for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
+ {
+ if (_gp_IsDFSTreeRoot(theGraph, v))
+ {
+ if (gp_GetVertexDegree(theGraph, v) > 0)
+ NumFaces--;
+ connectedComponents++;
+ }
+ }
+
+ NumFaces++;
+
+ return NumFaces == gp_GetM(theGraph) - gp_GetN(theGraph) + 1 + connectedComponents
+ ? NumFaces
+ : -1;
+}
+
+/********************************************************************
+ gp_CreateEmbeddingFaceList()
+
+ Creates a caller-owned YAML string containing one face list section per
+ connected component of a graph structure containing a planar embedding.
+ Returns OK on success, NOTOK on failure.
+ ********************************************************************/
+
+int gp_CreateEmbeddingFaceList(graphP theGraph, char **pFaceList)
+{
+ stackP theStack;
+ strBufP faceList = NULL;
+ int *visitedVertices = NULL, *componentEdges = NULL;
+ int componentEdgesCapacity, componentNumber = 0, Result = OK;
+ int lowerVertex, upperVertex, v;
+
+ if (theGraph == NULL || pFaceList == NULL || *pFaceList != NULL)
+ return NOTOK;
+
+ if (!(gp_GetGraphFlags(theGraph) & GRAPHFLAGS_DFSNUMBERED) ||
+ gp_CountEmbeddingFaces(theGraph) < 0 ||
+ _ClearEmbeddingFaceEdgeVisitedFlags(theGraph) != OK)
+ {
+ return NOTOK;
+ }
+
+ lowerVertex = gp_LowerBoundVertices(theGraph);
+ upperVertex = gp_UpperBoundVertices(theGraph);
+ theStack = theGraph->theStack;
+ componentEdgesCapacity = 2 * gp_GetM(theGraph);
+ if (componentEdgesCapacity == 0)
+ componentEdgesCapacity = 1;
+
+ faceList = sb_New(0);
+ visitedVertices = (int *)calloc((size_t)gp_UpperBoundVertexStorage(theGraph), sizeof(int));
+ componentEdges = (int *)calloc((size_t)componentEdgesCapacity, sizeof(int));
+
+ if (faceList == NULL || visitedVertices == NULL || componentEdges == NULL)
+ {
+ Result = NOTOK;
+ goto gp_CreateEmbeddingFaceList_Cleanup;
+ }
+
+ for (v = lowerVertex; v < upperVertex; ++v)
+ {
+ int componentEdgeCount = 0, i;
+
+ if (visitedVertices[v])
+ continue;
+
+ componentNumber++;
+ if (_AppendEmbeddingFaceListHeader(faceList, componentNumber) != OK)
+ {
+ Result = NOTOK;
+ goto gp_CreateEmbeddingFaceList_Cleanup;
+ }
+
+ sp_ClearStack(theStack);
+ sp_Push(theStack, v);
+
+ while (sp_NonEmpty(theStack))
+ {
+ int u, e;
+
+ sp_Pop(theStack, u);
+ if (visitedVertices[u])
+ continue;
+
+ visitedVertices[u] = TRUE;
+ e = gp_GetFirstEdge(theGraph, u);
+ while (gp_IsEdge(theGraph, e))
+ {
+ if (componentEdgeCount >= componentEdgesCapacity)
+ goto gp_CreateEmbeddingFaceList_Cleanup;
+
+ componentEdges[componentEdgeCount++] = e;
+
+ if (!visitedVertices[gp_GetNeighbor(theGraph, e)])
+ sp_Push(theStack, gp_GetNeighbor(theGraph, e));
+
+ e = gp_GetNextEdge(theGraph, e);
+ }
+ }
+
+ for (i = 0; i < componentEdgeCount; ++i)
+ {
+ if (!gp_GetEdgeVisited(theGraph, componentEdges[i]) &&
+ _AppendEmbeddingFace(faceList, theGraph, componentEdges[i]) != OK)
+ {
+ Result = NOTOK;
+ goto gp_CreateEmbeddingFaceList_Cleanup;
+ }
+ }
+ }
+
+ *pFaceList = sb_TakeString(faceList);
+ Result = *pFaceList == NULL ? NOTOK : OK;
+
+gp_CreateEmbeddingFaceList_Cleanup:
+
+ if (componentEdges != NULL)
+ {
+ free(componentEdges);
+ componentEdges = NULL;
+ }
+
+ if (visitedVertices != NULL)
+ {
+ free(visitedVertices);
+ visitedVertices = NULL;
+ }
+
+ sb_Free(&faceList);
+
+ if (Result != OK && pFaceList != NULL)
+ {
+ if (*pFaceList != NULL)
+ {
+ free(*pFaceList);
+ *pFaceList = NULL;
+ }
+ }
+
+ return Result;
+}
diff --git a/planarity/c/graphLib/planarityRelated/graphTests.c b/planarity/c/graphLib/planarityRelated/graphTests.c
index c59f382..3db1ebe 100644
--- a/planarity/c/graphLib/planarityRelated/graphTests.c
+++ b/planarity/c/graphLib/planarityRelated/graphTests.c
@@ -13,7 +13,7 @@ See the LICENSE.TXT file for licensing information.
#include "graphPlanarity.h"
#include "graphPlanarity.private.h"
-extern void _ClearVertexVisitedFlags(graphP theGraph, int);
+extern void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices);
/* Private function declarations (some exported to system) */
@@ -172,92 +172,7 @@ int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
int _CheckEmbeddingFacialIntegrity(graphP theGraph)
{
- stackP theStack = theGraph->theStack;
- int 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 edge records (both parts of each edge) and set them all to unvisited */
-
- for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph); e += 2)
- {
- // Except skip edge holes
- if (gp_EdgeInUse(theGraph, e))
- {
- // Push e and clear its visited flag
- sp_Push(theStack, e);
- gp_ClearEdgeVisited(theGraph, e);
-
- // And now the same for the twin of e
- eTwin = gp_GetTwin(theGraph, e);
- sp_Push(theStack, eTwin);
- gp_ClearEdgeVisited(theGraph, eTwin);
- }
- }
-
- // There are M edges, so we better have pushed 2M edge records,
- // i.e., we test that the continue above skipped edge holes and
- // didn't push edge records beyond the boundary of those in use.
- if (sp_GetCurrentSize(theStack) != 2 * gp_GetM(theGraph))
- return NOTOK;
-
- /* Read faces until every edge record is popped */
-
- NumFaces = 0;
- while (sp_NonEmpty(theStack))
- {
- /* Get an edge record; 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_GetNextEdgeCircular(theGraph, gp_GetTwin(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_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++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 == gp_GetM(theGraph) - gp_GetN(theGraph) + 1 + connectedComponents
- ? OK
- : NOTOK;
+ return gp_CountEmbeddingFaces(theGraph) < 0 ? NOTOK : OK;
}
/********************************************************************
@@ -282,7 +197,7 @@ int _CheckAllVerticesOnExternalFace(graphP theGraph)
// mark the vertices as visited
for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
{
- if (gp_IsDFSTreeRoot(theGraph, v))
+ if (_gp_IsDFSTreeRoot(theGraph, v))
_MarkExternalFaceVertices(theGraph, v);
}
diff --git a/planarity/express/cplanarity.pxd b/planarity/express/cplanarity.pxd
index a5be344..1000985 100644
--- a/planarity/express/cplanarity.pxd
+++ b/planarity/express/cplanarity.pxd
@@ -45,6 +45,7 @@ cdef extern from "../c/graphLib/io/graphIO.h":
int WRITE_ADJLIST
int WRITE_ADJMATRIX
int WRITE_G6
+ int WRITE_GRAPHML
int gp_Write(graphP theGraph, char *FileName, int Mode)
diff --git a/planarity/express/planarity.pyx b/planarity/express/planarity.pyx
index c9015c6..af8584a 100644
--- a/planarity/express/planarity.pyx
+++ b/planarity/express/planarity.pyx
@@ -715,8 +715,8 @@ cdef class PGraph:
) -> None:
"""Writes the graph to ``path``.
- Supports writing in formats: ``WRITE_ADJLIST``, ``WRITE_ADJMATRIX``, and
- ``WRITE_G6``.
+ Supports writing in formats: ``WRITE_ADJLIST``, ``WRITE_ADJMATRIX``,
+ ``WRITE_G6``, and ``WRITE_GRAPHML``.
Args:
path (str): Path to which to write graph. Defaults to ``stdout``
diff --git a/planarity/full/cgraphLib.pxd b/planarity/full/cgraphLib.pxd
index a82e620..a7561ac 100644
--- a/planarity/full/cgraphLib.pxd
+++ b/planarity/full/cgraphLib.pxd
@@ -182,7 +182,7 @@ cdef extern from "../c/graphLib/io/graphIO.h":
int gp_Write(graphP theGraph, char *fileName, int writeMode)
int gp_WriteToString(graphP theGraph, char **pOutputStr, int writeMode)
- int WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_DEBUGINFO, WRITE_G6
+ int WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_DEBUGINFO, WRITE_G6, WRITE_GRAPHML
int GRAPHFLAGS_ZEROBASEDIO
diff --git a/planarity/full/graph.pyx b/planarity/full/graph.pyx
index d79dd38..8d71767 100644
--- a/planarity/full/graph.pyx
+++ b/planarity/full/graph.pyx
@@ -36,6 +36,7 @@ AT_EDGE_CAPACITY_LIMIT = graphLib.AT_EDGE_CAPACITY_LIMIT
WRITE_ADJLIST = graphLib.WRITE_ADJLIST
WRITE_ADJMATRIX = graphLib.WRITE_ADJMATRIX
WRITE_G6 = graphLib.WRITE_G6
+WRITE_GRAPHML = graphLib.WRITE_GRAPHML
EMBEDFLAGS_PLANAR = graphLib.EMBEDFLAGS_PLANAR
@@ -1832,15 +1833,15 @@ cdef class Graph:
fileName: a string containing the name of the file to which to
write.
writeMode: the desired output format, i.e., ``WRITE_ADJLIST``,
- ``WRITE_ADJMATRIX``, or ``WRITE_G6``.
+ ``WRITE_ADJMATRIX``, ``WRITE_G6``, or ``WRITE_GRAPHML``.
Raises:
ValueError: if ``writeMode`` is not ``WRITE_ADJLIST``,
- ``WRITE_ADJMATRIX``, or ``WRITE_G6``.
+ ``WRITE_ADJMATRIX``, ``WRITE_G6``, or ``WRITE_GRAPHML``.
RuntimeError: if the C-layer ``graphLib`` version of this function
fails.
"""
- if writeMode not in (WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_G6):
+ if writeMode not in (WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_G6, WRITE_GRAPHML):
raise ValueError(
f"gp_Write() failed: invalid writeMode = {writeMode}"
)
@@ -1860,7 +1861,7 @@ cdef class Graph:
Args:
writeMode: the desired output format, i.e., ``WRITE_ADJLIST``,
- ``WRITE_ADJMATRIX``, or ``WRITE_G6``.
+ ``WRITE_ADJMATRIX``, ``WRITE_G6``, or ``WRITE_GRAPHML``.
Returns:
A Python string containing the graph serialized into the chosen
@@ -1868,12 +1869,12 @@ cdef class Graph:
Raises:
ValueError: if writeMode is not ``WRITE_ADJLIST``,
- ``WRITE_ADJMATRIX``, or ``WRITE_G6``.
+ ``WRITE_ADJMATRIX``, ``WRITE_G6``, or ``WRITE_GRAPHML``.
RuntimeError: if the C-layer ``graphLib`` version of this function
fails, if the ``outputString`` is ``NULL``, or if decoding the
bytes to produce the Python string fails.
"""
- if writeMode not in (WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_G6):
+ if writeMode not in (WRITE_ADJLIST, WRITE_ADJMATRIX, WRITE_G6, WRITE_GRAPHML):
raise ValueError(
f"gp_WriteToString() failed: invalid writeMode = {writeMode}"
)
diff --git a/planarity/full/graphLib.pyx b/planarity/full/graphLib.pyx
index 08364af..c6c8b51 100644
--- a/planarity/full/graphLib.pyx
+++ b/planarity/full/graphLib.pyx
@@ -54,6 +54,7 @@ AT_EDGE_CAPACITY_LIMIT = cgraphLib.AT_EDGE_CAPACITY_LIMIT
WRITE_ADJLIST = cgraphLib.WRITE_ADJLIST
WRITE_ADJMATRIX = cgraphLib.WRITE_ADJMATRIX
WRITE_G6 = cgraphLib.WRITE_G6
+WRITE_GRAPHML = cgraphLib.WRITE_GRAPHML
GRAPHFLAGS_ZEROBASEDIO = cgraphLib.GRAPHFLAGS_ZEROBASEDIO
diff --git a/tests/test_express_api_write.py b/tests/test_express_api_write.py
index c0d3f50..8438fdb 100644
--- a/tests/test_express_api_write.py
+++ b/tests/test_express_api_write.py
@@ -35,11 +35,24 @@ def test_write_g6(self):
assert d == answer
os.unlink(fname)
- def test_write_function_g6(self):
+ def test_write_graphml(self):
e = ([1,2],)
fname = tempfile.mktemp()
- planarity.write(e, fname, planarity.WRITE_G6)
+ planarity.write(e, fname, planarity.WRITE_GRAPHML)
d = open(fname).read()
- answer = '>>graph6<\n' \
+ ' \n' \
+ ' \n' \
+ ' \n' \
+ ' \n' \
+ ' \n' \
+ '\n'
assert d == answer
os.unlink(fname)