Path: blob/master/sage/graphs/planarity/graphExtensions.c
4057 views
/*1Planarity-Related Graph Algorithms Project2Copyright (c) 1997-2010, John M. Boyer3All rights reserved. Includes a reference implementation of the following:45* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,6Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,7University of Victoria, 2001.89* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)10Planarity by Edge Addition". Journal of Graph Algorithms and Applications,11Vol. 8, No. 3, pp. 241-273, 2004.1213* John M. Boyer. "A New Method for Efficiently Generating Planar Graph14Visibility Representations". In P. Eades and P. Healy, editors,15Proceedings of the 13th International Conference on Graph Drawing 2005,16Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.1718Redistribution and use in source and binary forms, with or without modification,19are permitted provided that the following conditions are met:2021* Redistributions of source code must retain the above copyright notice, this22list of conditions and the following disclaimer.2324* Redistributions in binary form must reproduce the above copyright notice, this25list of conditions and the following disclaimer in the documentation and/or26other materials provided with the distribution.2728* Neither the name of the Planarity-Related Graph Algorithms Project nor the names29of its contributors may be used to endorse or promote products derived from this30software without specific prior written permission.3132THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"33AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE34IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE35DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR36ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES37(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;38LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON39ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT40(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS41SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.42*/4344#include <stdlib.h>45#include <string.h>4647#include "appconst.h"4849#include "graphExtensions.private.h"50#include "graphExtensions.h"51#include "graphFunctionTable.h"5253/* Imported functions */5455extern void _InitFunctionTable(graphP theGraph);5657/* Private function */5859void _FreeExtension(graphExtensionP extension);60void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions);61void _FixupFunctionTables(graphP theGraph, graphExtensionP curr);62graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex);6364/********************************************************************65* The moduleIDGenerator is used to help ensure that all extensions66* added during a run-time have a different integer identifier.67* An ID identifies an extension, which may be added to multiple68* graphs. It is used in lieu of identifying extensions by a string69* name, which is noticeably expensive when a frequently called70* overload function seeks the extension context for a graph.71********************************************************************/7273static int moduleIDGenerator = 0;7475/********************************************************************76The extension mechanism allows new modules to equip a graph with the77data structures and functions needed to implement new algorithms78without impeding the performance of the core graph planar embedding79algorithms on graphs that have not been so equipped.8081The following steps must be used to create a graph extension:82831) Create a moduleID variable initialized to zero that will be84assigned a positive integer the first time the extension is85added to a graph by gp_AddExtension()86872) Define an extension context structure to contain all of the data88and function pointers that extend the graph. The context must89include a graphFunctionTable to allow overloading of functions.90An instance of this context structure is passed to the "context"91parameter of gp_AddExtension().92933) Define a function capable of duplicating your context data94structure. It receives a void pointer indicating the context95to duplicate and a void pointer that can be cast to a graph96pointer indicating the graph for which the context is being97duplicated. The void pointer returned indicates the newly98allocated context structure. The pointer to this function is99passed to the "dupContext" parameter of gp_AddExtension()100101Note: It is useful to store in your context structure a pointer102to the graph that the context is extending. There are certain103function overloads you will perform that will only receive104the context, and you may need to know things about the graph,105such as the number of vertices or edges.1061074) Define a function that can free the memory used by your context108data structure. It will receive a void pointer indicating the109instance of your context data structure that you passed as the110"context" parameter to gp_AddExtension().111The free function pointer should be passed as the "freeContext"112parameter to gp_AddExtension()1131145) The expected method of attaching your feature to a graph is to115create a function called gp_AttachFeature(), where 'Feature' is116the name of your module. The attach function allocates your context117data structure, initializes the extension data, assigns overload118function pointers, and invokes gp_AddExtension().119120NOTE: It is advisable to use memset on the context function table121before assigning any function overloads because any function not122being overloaded must have a NULL pointer.123124NOTE: The gp_AddExtension() method puts the overload function125pointers into the graph's function table, and the base function126pointers that were overloaded are placed in the context function127table. This allows the extension's functions to have access to128base function behaviors, since many extension functions will129extend rather than replace the base behavior.1301316) There are a few functions that you must overload in order to132successfully manage data structures that are parallel to the133main graph data structures.134135The core graph data structure has function pointers to functions136that can be overloaded. In addition to invoking gp_AddExtension(),137you need to set pointers to your own versions of the functions138you are overloading. You will also need to store a copy of the139prior pointer in your feature's context data structure so that you140can invoke the "base" behavior from your function overload, e.g.141if your feature is attached but not active or if your feature142augments the base behavior rather than replacing it.143144a) If any kind of data structures need to be maintained at145the graph, vertex or graph node levels, then an overload146of fpInitGraph() will be needed.147148b) If any vertex-level data is needed, then an overload of149fpInitVertexRec() is needed.150This overload function should be named _Feature_InitVertexRec().151It will invoke the base fpVertexRec() and also invoke152a second function named_InitFeatureVertexRec() that153initializes the custom VertexRec data members154155c) If any graph node level data is needed, then an overload156of fpInitGraphNode() is needed.157This overload function should be named _Feature_InitGraphNode().158It will invoke the base fpInitGraphNode() and also invoke159a second function named_InitFeatureGraphNode() that160initializes the custom graph node data members161162d) If any graph-level data structures are needed, then an163overload of fpReinitializeGraph() will be needed. If only164vertex-level or graph node level data members are needed, then165the overloads of fpInitGraphNode() and fpInitVertexRec() are166invoked by the basic fpReinitializeGraph without needing to167overload it as well.168169e) If any data must be persisted in the file format, then overloads170of fpReadPostprocess() and fpWritePostprocess() are needed.1711727) Define internal functions for _Feature_ClearStructures(),173_Feature_CreateStructures() and _Feature_InitStructures();174175a) The _Feature_ClearStructures() should simply null out pointers176to extra structures on its first invocation, but thereafter it177should free them and then null them. Since the null-only step178is done only once in gp_AttachFeature(), it seems reasonable to179not bother with a more complicated _Feature_ClearStructures().180But, as an extension is developed, the data structures change,181so it is best to keep all this logic in one place.182183b) The _Feature_CreateStructures() should just allocate memory for184but not initialize any vertex level and graph node level data185structures. Data structures maintained at the graph level, such186as a stack or a list collection, should be created and initialized.187188c) The _Feature_InitStructures() should invoke just the functions189needed to initialize the custom VertexRec and GraphNode data190members, if any.1911928) Define a function gp_DetachFeature() that invokes gp_RemoveExtension()193This should be done for consistency, so that users of a feature194do not attach it with gp_AttachFeature() and remove it with195gp_RemoveExtension(). However, it may sometimes be necessary to196run more code than just gp_RemoveExtension() when detaching a feature,197e.g. some final result values of a feature may be saved to data198available in the core graph or in other features.199********************************************************************/200201/********************************************************************202gp_AddExtension()203204@param theGraph - pointer to the graph to which the extension is being added205@param pModuleID - address of the variable that contains the feature's206extension identifier. If the variable is equal to zero,207it is assigned a positive number. Thereafter, the variable208value can be used to find and remove the extension from any graph209@param context - the data storage for the extension being added210The context is owned by the extension and freed with freeContext()211@param dupContext - a function capable of duplicating the context data212@param freeContext - a function capable of freeing the context data213@param functions - pointer to a table of functions stored in the data context.214The table of functions is an input and output parameter.215On input, the table consists of new function pointers216for functions being overloaded.217Any function not being overloaded must be NULL.218The non-NULL function pointers are used to overload219the functions in the graph, and the prior pointer values220in the graph are stored in the function table as output.221The context data therefore has the pointer to the base222function corresponding to any function its extension223module overloaded.224225The new extension is created and added to the graph.226********************************************************************/227228int gp_AddExtension(graphP theGraph,229int *pModuleID,230void *context,231void *(*dupContext)(void *, void *),232void (*freeContext)(void *),233graphFunctionTableP functions)234{235graphExtensionP newExtension = NULL;236237if (theGraph == NULL || pModuleID == NULL ||238context == NULL || dupContext == NULL || freeContext == NULL ||239functions == NULL)240{241return NOTOK;242}243244// If the extension already exists, then don't redefine it.245if (gp_FindExtension(theGraph, *pModuleID, NULL) == TRUE)246{247return NOTOK;248}249250// Assign a unique ID to the extension if it does not already have one251if (*pModuleID == 0)252{253*pModuleID = ++moduleIDGenerator;254}255256// Allocate the new extension257if ((newExtension = (graphExtensionP) malloc(sizeof(graphExtension))) == NULL)258{259return NOTOK;260}261262// Assign the data payload of the extension263newExtension->moduleID = *pModuleID;264newExtension->context = context;265newExtension->dupContext = dupContext;266newExtension->freeContext = freeContext;267newExtension->functions = functions;268269_OverloadFunctions(theGraph, functions);270271// Make the new linkages272newExtension->next = (struct graphExtension *) theGraph->extensions;273theGraph->extensions = newExtension;274275// The new extension was successfully added276return OK;277278}279280/********************************************************************281_OverloadFunctions()282For each non-NULL function pointer, the pointer becomes the new value283for the function in the graph, and the old function pointer in the graph284is placed in the overload table.285286This way, when an extension function is invoked, it can choose to invoke287the base function before or after whatever extension behavior it provides.288289Also, when it comes time to remove an extension, this extension system290has access to the overload tables of all extensions so that it can unhook291the functions of the module being removed from the chains of calls for292each overloaded function. This will involve some pointer changes in293the overload tables of extensions other than the one being removed.294********************************************************************/295296void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions)297{298void **graphFunctionTable = (void **) &theGraph->functions;299void **newFunctionTable = (void **) functions;300int numFunctions = sizeof(theGraph->functions) / sizeof(void *);301int I;302303for (I = 0; I < numFunctions; I++)304{305if (newFunctionTable[I] != NULL)306{307void *fp = graphFunctionTable[I];308graphFunctionTable[I] = newFunctionTable[I];309newFunctionTable[I] = fp;310}311}312}313314/********************************************************************315gp_FindExtension()316317@param theGraph - the graph whose extension list is to be searched318@param moduleID - the identifier of the module whose extension context is desired319@param pContext - the return parameter that receives the value of the320extension, if found. This may be NULL if the extension was321not found or if the extension context value was NULL.322@return TRUE if the extension was found, NOTOK if not found323If FALSE is returned, then the context returned is guaranteed to be NULL324If TRUE is returned, the context returned may be NULL if that is the325current value of the module extension326********************************************************************/327328int gp_FindExtension(graphP theGraph, int moduleID, void **pContext)329{330graphExtensionP first = NULL, next = NULL;331332if (pContext != NULL)333{334*pContext = NULL;335}336337if (theGraph==NULL || moduleID==0)338{339return FALSE;340}341342first = theGraph->extensions;343344while (first != NULL)345{346next = (graphExtensionP) first->next;347if (first->moduleID == moduleID)348{349if (pContext != NULL)350{351*pContext = first->context;352}353return TRUE;354}355first = next;356}357358return FALSE;359}360361/********************************************************************362gp_GetExtension()363364Calling this function is equivalent to invoking gp_FindExtension()365except that some debuggers have difficulty stepping into a function366that (properly) start by setting a local variable pointer to NULL367when the debugger has watch expressions that dereference a pointer368of the same name. In such cases,369370MyContext *context = NULL;371gp_FindExtension(theGraph, MYEXTENSION_ID, &context);372373can be replaced by374375MyContext *context = gp_GetExtension(theGraph, MYEXTENSION_ID);376377@param theGraph - the graph whose extension list is to be searched378@param moduleID - the identifier of the module whose extension context is desired379@return void pointer to the extension if found, or NULL if not found.380********************************************************************/381void *gp_GetExtension(graphP theGraph, int moduleID)382{383void *context = NULL;384int result = gp_FindExtension(theGraph, moduleID, &context);385return result ? context : NULL;386}387388/********************************************************************389gp_RemoveExtension()390@param theGraph - the graph from which to remove an extension391@param moduleID - the ID of the module whose extension context is to be removed392@return OK if the module is successfully removed or not in the list393NOTOK for internal errors, such as invalid parameters394********************************************************************/395int gp_RemoveExtension(graphP theGraph, int moduleID)396{397graphExtensionP prev, curr, next;398399if (theGraph==NULL || moduleID==0)400return NOTOK;401402prev = NULL;403curr = theGraph->extensions;404405while (curr != NULL)406{407next = (graphExtensionP) curr->next;408409if (curr->moduleID == moduleID)410break;411412prev = curr;413curr = next;414}415416// An extension can only be removed if it is found. Otherwise,417// we return OK because the extension degenerately removed418// (since it is already gone)419if (curr != NULL)420{421_FixupFunctionTables(theGraph, curr);422423// Unhook the curr extension424if (prev != NULL)425prev->next = (struct graphExtension *) next;426else theGraph->extensions = next;427428// Free the curr extension429_FreeExtension(curr);430}431432return OK;433}434435436/********************************************************************437_FixupFunctionTables()438439Removes the functions in the curr function table from the function440call lists established by the function tables of all extensions and441theGraph.442443Since new extensions are prepended, extensions before curr may444have further overloaded the functions in the curr function table.445446For a non-NULL function pointer in the curr table, if there is447a preceding extension with the same function pointer non-NULL, then448the function table of the closest such preceding extension points449to the original overload function of the curr extension, and the450curr extension contains the pointer to the base function behavior,451so now the function table of that preceding extension must be changed452to the function pointer value in the curr extension.453********************************************************************/454455void _FixupFunctionTables(graphP theGraph, graphExtensionP curr)456{457void **currFunctionTable = (void **) (curr->functions);458int numFunctions = sizeof(*(curr->functions)) / sizeof(void *);459int I;460461for (I = 0; I < numFunctions; I++)462{463if (currFunctionTable[I] != NULL)464{465void **nearestOverloadFunctionTable = (void **) &theGraph->functions;466graphExtensionP pred = _FindNearestOverload(theGraph, curr, I);467468if (pred != NULL)469nearestOverloadFunctionTable = (void **) pred->functions;470471nearestOverloadFunctionTable[I] = currFunctionTable[I];472}473}474}475476/********************************************************************477_FindNearestOverload()478********************************************************************/479480graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex)481{482graphExtensionP curr = theGraph->extensions;483graphExtensionP found = NULL;484void **functionTable;485486while (curr != target)487{488functionTable = (void **) curr->functions;489if (functionTable[functionIndex] != NULL)490found = curr;491492curr = (graphExtensionP) curr->next;493}494495return found;496}497498/********************************************************************499gp_CopyExtensions()500********************************************************************/501502int gp_CopyExtensions(graphP dstGraph, graphP srcGraph)503{504graphExtensionP next = NULL, newNext = NULL, newLast = NULL;505506if (srcGraph == NULL || dstGraph == NULL)507return NOTOK;508509gp_FreeExtensions(dstGraph);510511next = srcGraph->extensions;512513while (next != NULL)514{515if ((newNext = (graphExtensionP) malloc(sizeof(graphExtension))) == NULL)516{517gp_FreeExtensions(dstGraph);518return NOTOK;519}520521newNext->moduleID = next->moduleID;522newNext->context = next->dupContext(next->context, dstGraph);523newNext->dupContext = next->dupContext;524newNext->freeContext = next->freeContext;525newNext->functions = next->functions;526newNext->next = NULL;527528if (newLast != NULL)529newLast->next = (struct graphExtension *) newNext;530else531dstGraph->extensions = newNext;532533newLast = newNext;534next = (graphExtensionP) next->next;535}536537return OK;538}539540/********************************************************************541gp_FreeExtensions()542543@param pFirst - pointer to head pointer of graph extension list544545Each graph extension is freed, including invoking the freeContext546function provided when the extension was added.547********************************************************************/548549void gp_FreeExtensions(graphP theGraph)550{551if (theGraph != NULL)552{553graphExtensionP curr = theGraph->extensions;554graphExtensionP next = NULL;555556while (curr != NULL)557{558next = (graphExtensionP) curr->next;559_FreeExtension(curr);560curr = next;561}562563theGraph->extensions = NULL;564_InitFunctionTable(theGraph);565}566}567568/********************************************************************569_FreeExtension()570********************************************************************/571void _FreeExtension(graphExtensionP extension)572{573if (extension->context != NULL && extension->freeContext != NULL)574{575extension->freeContext(extension->context);576}577free(extension);578}579580581