/*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 "graph.h"4849/* Private functions (exported to system) */5051int _ReadAdjMatrix(graphP theGraph, FILE *Infile);52int _ReadAdjList(graphP theGraph, FILE *Infile);53int _WriteAdjList(graphP theGraph, FILE *Outfile);54int _WriteAdjMatrix(graphP theGraph, FILE *Outfile);55int _WriteDebugInfo(graphP theGraph, FILE *Outfile);5657/********************************************************************58_ReadAdjMatrix()59This function reads the undirected graph in upper triangular matrix format.60Though O(N^2) time is required, this routine is useful during61reliability testing due to the wealth of graph generating software62that uses this format for output.63Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges64********************************************************************/6566int _ReadAdjMatrix(graphP theGraph, FILE *Infile)67{68int N, I, W, Flag, ErrorCode;6970if (Infile == NULL) return NOTOK;71fscanf(Infile, " %d ", &N);72if (gp_InitGraph(theGraph, N) != OK)73return NOTOK;7475for (I = 0, ErrorCode = OK; I < N-1 && ErrorCode==OK; I++)76{77theGraph->G[I].v = I;78for (W = I+1; W < N; W++)79{80fscanf(Infile, " %1d", &Flag);81if (Flag)82{83ErrorCode = gp_AddEdge(theGraph, I, 0, W, 0);84if (ErrorCode != OK) break;85}86}87}8889return ErrorCode;90}9192/********************************************************************93_ReadAdjList()94This function reads the graph in adjacency list format.9596The file format is97On the first line : N= number of vertices98On N subsequent lines: #: a b c ... -199where # is a vertex number and a, b, c, ... are its neighbors.100101NOTE: The vertex number is for file documentation only. It is an102error if the vertices are not in sorted order in the file.103104NOTE: If a loop edge is found, it is ignored without error.105106NOTE: This routine supports digraphs. For a directed arc (I -> W),107an edge record is created in both vertices, I and W, and the108edge record in I's adjacency list is marked OUTONLY while the109edge record in W's list is marked INONLY.110This makes it easy to used edge directedness when appropriate111but also seamlessly process the corresponding undirected graph.112113Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges114********************************************************************/115116int _ReadAdjList(graphP theGraph, FILE *Infile)117{118int N, I, W, ErrorCode, adjList, J;119120if (Infile == NULL) return NOTOK;121fgetc(Infile); /* Skip the N= */122fgetc(Infile);123fscanf(Infile, " %d ", &N); /* Read N */124if (gp_InitGraph(theGraph, N) != OK)125{126printf("Failed to init graph");127return NOTOK;128}129130// Clear the visited members of the vertices so they can be used131// during the adjacency list read operation132for (I=0; I < N; I++)133theGraph->G[I].visited = 0;134135// Do the adjacency list read operation for each vertex in order136for (I = 0, ErrorCode = OK; I < N && ErrorCode==OK; I++)137{138// Read the vertex number139fscanf(Infile, "%d", &theGraph->G[I].v);140141// The vertices are expected to be in numeric ascending order142if (theGraph->G[I].v != I)143return NOTOK;144145// Skip the colon after the vertex number146fgetc(Infile);147148// If the vertex already has a non-empty adjacency list, then it is149// the result of adding edges during processing of preceding vertices.150// The list is removed from the current vertex I and saved for use151// during the read operation for I. Adjacencies to preceding vertices152// are pulled from this list, if present, or added as directed edges153// if not. Adjacencies to succeeding vertices are added as undirected154// edges, and will be corrected later if the succeeding vertex does not155// have the matching adjacency using the following mechanism. After the156// read operation for a vertex I, any adjacency nodes left in the saved157// list are converted to directed edges from the preceding vertex to I.158adjList = gp_GetFirstArc(theGraph, I);159if (gp_IsArc(theGraph, adjList))160{161// Store the adjacency node location in the visited member of each162// of the preceding vertices to which I is adjacent so that we can163// efficiently detect the adjacency during the read operation and164// efficiently find the adjacency node.165J = gp_GetFirstArc(theGraph, I);166while (gp_IsArc(theGraph, J))167{168theGraph->G[theGraph->G[J].v].visited = J;169J = gp_GetNextArc(theGraph, J);170}171172// Make the adjacency list circular, for later ease of processing173gp_SetPrevArc(theGraph, adjList, gp_GetLastArc(theGraph, I));174gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, I), adjList);175176// Remove the list from the vertex177gp_SetFirstArc(theGraph, I, gp_AdjacencyListEndMark(I));178gp_SetLastArc(theGraph, I, gp_AdjacencyListEndMark(I));179}180181// Read the adjacency list.182while (1)183{184// Read the next adjacent vertex, with NIL indicating the list end185fscanf(Infile, " %d ", &W);186if (W < 0) break;187188// Vertex numbers must be less than N189if (W >= N)190ErrorCode = NOTOK;191192// Loop edges are not supported, but no reason to throw an error if they occur193// If a loop occurs, we just do like the ostrich and ignore it194else if (W == I)195ErrorCode = OK;196197// If the adjacency is to a succeeding, higher numbered vertex,198// then we'll add an undirected edge for now199else if (I < W)200{201ErrorCode = gp_AddEdge(theGraph, I, 0, W, 0);202}203204// If the adjacency is to a preceding, lower numbered vertex, then205// we have to pull the adjacency node from the preexisting adjList,206// if it is there, and if not then we have to add a directed edge.207else208{209// If the adjacency node (arc) already exists, then we add it210// as the new first arc of the vertex and delete it from adjList211if (theGraph->G[W].visited)212{213J = theGraph->G[W].visited;214215// Remove the arc J from the adjList construct216theGraph->G[W].visited = 0;217if (adjList == J)218{219if ((adjList = gp_GetNextArc(theGraph, J)) == J)220adjList = NIL;221}222gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, J), gp_GetPrevArc(theGraph, J));223gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, J), gp_GetNextArc(theGraph, J));224225gp_AttachFirstArc(theGraph, I, J);226}227228// If an adjacency node to the lower numbered vertex W does not229// already exist, then we make a new directed arc from the current230// vertex I to W.231else232{233// It is added as the new first arc in both vertices234ErrorCode = gp_AddEdge(theGraph, I, 0, W, 0);235if (ErrorCode == OK)236// Note that this call also sets OUTONLY on the twin arc237gp_SetDirection(theGraph, gp_GetFirstArc(theGraph, W), EDGEFLAG_DIRECTION_INONLY);238}239}240241if (ErrorCode != OK) break;242}243244// If there are still adjList entries after the read operation245// then those entries are not representative of full undirected edges.246// Rather, they represent incoming directed arcs from other vertices247// into vertex I. They need to be added back into I's adjacency list but248// marked as "INONLY", while the twin is marked "OUTONLY" (by the same function).249while (gp_IsArc(theGraph, adjList))250{251J = adjList;252253theGraph->G[theGraph->G[J].v].visited = 0;254255if ((adjList = gp_GetNextArc(theGraph, J)) == J)256adjList = NIL;257258gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, J), gp_GetPrevArc(theGraph, J));259gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, J), gp_GetNextArc(theGraph, J));260261gp_AttachFirstArc(theGraph, I, J);262gp_SetDirection(theGraph, J, EDGEFLAG_DIRECTION_INONLY);263}264}265266return ErrorCode;267}268269/********************************************************************270_ReadLEDAGraph()271Reads the edge list from a LEDA file containing a simple undirected graph.272********************************************************************/273274int _ReadLEDAGraph(graphP theGraph, FILE *Infile)275{276char Line[256];277int N, I, M, J, u, v;278279/* Skip the lines that say LEDA.GRAPH and give the node and edge types */280fgets(Line, 255, Infile);281fgets(Line, 255, Infile);282fgets(Line, 255, Infile);283284/* Read the number of vertices, then skip that many more lines. */285fgets(Line, 255, Infile);286sscanf(Line, " %d", &N);287for (I = 0; I < N; I++)288fgets(Line, 255, Infile);289290/* Initialize the graph */291if (gp_InitGraph(theGraph, N) != OK)292return NOTOK;293294/* Read the number of edges */295fgets(Line, 255, Infile);296sscanf(Line, " %d", &M);297298/* Read and add each edge, omitting duplicates */299for (J = 0; J < M; J++)300{301fgets(Line, 255, Infile);302sscanf(Line, " %d %d", &u, &v);303if (u != v && !gp_IsNeighbor(theGraph, u-1, v-1))304{305if (gp_AddEdge(theGraph, u-1, 0, v-1, 0) != OK)306return NOTOK;307}308}309310return OK;311}312313/********************************************************************314gp_Read()315Opens the given file, determines whether it is in adjacency list or316matrix format based on whether the file start with N or just a number,317calls the appropriate read function, then closes the file and returns318the graph.319320Digraphs and loop edges are not supported in the adjacency matrix format,321which is upper triangular.322323In the adjacency list format, digraphs are supported. Loop edges are324ignored without producing an error.325326Pass "stdin" for the FileName to read from the stdin stream327328Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges329********************************************************************/330331int gp_Read(graphP theGraph, char *FileName)332{333FILE *Infile;334char Ch;335int RetVal;336337if (strcmp(FileName, "stdin") == 0)338Infile = stdin;339else if ((Infile = fopen(FileName, READTEXT)) == NULL)340return NOTOK;341342Ch = (char) fgetc(Infile);343ungetc(Ch, Infile);344if (Ch == 'N')345RetVal = _ReadAdjList(theGraph, Infile);346else if (Ch == 'L')347RetVal = _ReadLEDAGraph(theGraph, Infile);348else RetVal = _ReadAdjMatrix(theGraph, Infile);349350if (RetVal == OK)351{352void *extraData = NULL;353long filePos = ftell(Infile);354long fileSize;355356fseek(Infile, 0, SEEK_END);357fileSize = ftell(Infile);358fseek(Infile, filePos, SEEK_SET);359360if (filePos < fileSize)361{362extraData = malloc(fileSize - filePos + 1);363fread(extraData, fileSize - filePos, 1, Infile);364}365/*// Useful for quick debugging of IO extensibility366if (extraData == NULL)367printf("extraData == NULL\n");368else369{370char *extraDataString = (char *) extraData;371extraDataString[fileSize - filePos] = '\0';372printf("extraData = '%s'\n", extraDataString);373}374*/375376if (extraData != NULL)377{378RetVal = theGraph->functions.fpReadPostprocess(theGraph, extraData, fileSize - filePos);379free((void *) extraData);380}381}382383if (strcmp(FileName, "stdin") != 0)384fclose(Infile);385386return RetVal;387}388389int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize)390{391return OK;392}393394/********************************************************************395_WriteAdjList()396For each vertex, we write its number, a colon, the list of adjacent vertices,397then a NIL. The vertices occupy the first N positions of theGraph. Each398vertex is also has indicators of the first and last adjacency nodes (arcs)399in its adjacency list.400401Returns: NOTOK if either param is NULL; OK otherwise (after printing402adjacency list representation to Outfile).403********************************************************************/404405int _WriteAdjList(graphP theGraph, FILE *Outfile)406{407int I, J;408409if (theGraph==NULL || Outfile==NULL) return NOTOK;410411fprintf(Outfile, "N=%d\n", theGraph->N);412for (I=0; I < theGraph->N; I++)413{414fprintf(Outfile, "%d:", I);415416J = gp_GetLastArc(theGraph, I);417while (gp_IsArc(theGraph, J))418{419if (!gp_GetDirection(theGraph, J, EDGEFLAG_DIRECTION_INONLY))420fprintf(Outfile, " %d", theGraph->G[J].v);421422J = gp_GetPrevArc(theGraph, J);423}424fprintf(Outfile, " %d\n", NIL);425}426return OK;427}428429/********************************************************************430_WriteAdjMatrix()431Outputs upper triangular matrix representation capable of being432read by _ReadAdjMatrix()433434Note: This routine does not support digraphs and will return an435error if a directed edge is found.436437returns OK for success, NOTOK for failure438********************************************************************/439440int _WriteAdjMatrix(graphP theGraph, FILE *Outfile)441{442int I, J, K;443char *Row = NULL;444445if (theGraph != NULL)446Row = (char *) malloc((theGraph->N+1)*sizeof(char));447448if (Row==NULL || theGraph==NULL || Outfile==NULL)449{450if (Row != NULL) free(Row);451return NOTOK;452}453454fprintf(Outfile, "%d\n", theGraph->N);455for (I = 0; I < theGraph->N; I++)456{457for (K = 0; K <= I; K++)458Row[K] = ' ';459for (K = I+1; K < theGraph->N; K++)460Row[K] = '0';461462J = gp_GetFirstArc(theGraph, I);463while (gp_IsArc(theGraph, J))464{465if (gp_GetDirection(theGraph, J, EDGEFLAG_DIRECTION_INONLY))466return NOTOK;467468if (theGraph->G[J].v > I)469Row[theGraph->G[J].v] = '1';470471J = gp_GetNextArc(theGraph, J);472}473474Row[theGraph->N] = '\0';475fprintf(Outfile, "%s\n", Row);476}477478free(Row);479return OK;480}481482/********************************************************************483_WriteDebugInfo()484Writes adjacency list, but also includes the type value of each485edge (e.g. is it DFS child arc, forward arc or back arc?), and486the L, A and DFSParent of each vertex.487********************************************************************/488489int _WriteDebugInfo(graphP theGraph, FILE *Outfile)490{491int I, J, Gsize;492493if (theGraph==NULL || Outfile==NULL) return NOTOK;494495/* Print parent copy vertices and their adjacency lists */496497fprintf(Outfile, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M);498for (I=0; I < theGraph->N; I++)499{500fprintf(Outfile, "%d(P=%d,lA=%d,LowPt=%d,v=%d):",501I, theGraph->V[I].DFSParent,502theGraph->V[I].leastAncestor,503theGraph->V[I].Lowpoint,504theGraph->G[I].v);505506J = gp_GetFirstArc(theGraph, I);507while (gp_IsArc(theGraph, J))508{509fprintf(Outfile, " %d(J=%d)", theGraph->G[J].v, J);510J = gp_GetNextArc(theGraph, J);511}512513fprintf(Outfile, " %d\n", NIL);514}515516/* Print any root copy vertices and their adjacency lists */517518for (I = theGraph->N; I < 2*theGraph->N; I++)519{520if (theGraph->G[I].v == NIL)521continue;522523fprintf(Outfile, "%d(copy of=%d, DFS child=%d):",524I, theGraph->G[I].v, I-theGraph->N);525526J = gp_GetFirstArc(theGraph, I);527while (gp_IsArc(theGraph, J))528{529fprintf(Outfile, " %d(J=%d)", theGraph->G[J].v, J);530J = gp_GetNextArc(theGraph, J);531}532533fprintf(Outfile, " %d\n", NIL);534}535536/* Print information about vertices and root copy (virtual) vertices */537fprintf(Outfile, "\nVERTEX INFORMATION\n");538for (I=0; I < 2*theGraph->N; I++)539{540if (theGraph->G[I].v == NIL)541continue;542543fprintf(Outfile, "V[%3d] v=%3d, type=%c, first arc=%3d, last arc=%3d\n",544I,545theGraph->G[I].v,546theGraph->G[I].type,547gp_GetFirstArc(theGraph, I),548gp_GetLastArc(theGraph, I));549}550551/* Print information about edges */552553fprintf(Outfile, "\nEDGE INFORMATION\n");554Gsize = theGraph->edgeOffset + theGraph->arcCapacity;555for (J=theGraph->edgeOffset; J < Gsize; J++)556{557if (theGraph->G[J].v == NIL)558continue;559560fprintf(Outfile, "E[%3d] v=%3d, type=%c, next arc=%3d, prev arc=%3d\n",561J,562theGraph->G[J].v,563theGraph->G[J].type,564gp_GetNextArc(theGraph, J),565gp_GetPrevArc(theGraph, J));566}567568return OK;569}570571/********************************************************************572gp_Write()573Writes theGraph into the file.574Pass "stdout" or "stderr" to FileName to write to the corresponding stream575Pass WRITE_ADJLIST, WRITE_ADJMATRIX or WRITE_DEBUGINFO for the Mode576577NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST578579Returns NOTOK on error, OK on success.580********************************************************************/581582int gp_Write(graphP theGraph, char *FileName, int Mode)583{584FILE *Outfile;585int RetVal;586587if (theGraph == NULL || FileName == NULL)588return NOTOK;589590if (strcmp(FileName, "nullwrite") == 0)591return OK;592593if (strcmp(FileName, "stdout") == 0)594Outfile = stdout;595else if (strcmp(FileName, "stderr") == 0)596Outfile = stderr;597else if ((Outfile = fopen(FileName, WRITETEXT)) == NULL)598return NOTOK;599600switch (Mode)601{602case WRITE_ADJLIST :603RetVal = _WriteAdjList(theGraph, Outfile);604break;605case WRITE_ADJMATRIX :606RetVal = _WriteAdjMatrix(theGraph, Outfile);607break;608case WRITE_DEBUGINFO :609RetVal = _WriteDebugInfo(theGraph, Outfile);610break;611default :612RetVal = NOTOK;613break;614}615616if (RetVal == OK)617{618void *extraData = NULL;619long extraDataSize;620621RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData, &extraDataSize);622623if (extraData != NULL)624{625if (!fwrite(extraData, extraDataSize, 1, Outfile))626RetVal = NOTOK;627free(extraData);628}629}630631if (strcmp(FileName, "stdout") == 0 || strcmp(FileName, "stderr") == 0)632fflush(Outfile);633634else if (fclose(Outfile) != 0)635RetVal = NOTOK;636637return RetVal;638}639640/********************************************************************641_WritePostprocess()642643By default, no additional information is written.644********************************************************************/645646int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize)647{648return OK;649}650651/********************************************************************652_Log()653654When the project is compiled with LOGGING enabled, this method writes655a string to the file PLANARITY.LOG in the current working directory.656On first write, the file is created or cleared.657Call this method with NULL to close the log file.658********************************************************************/659660void _Log(char *Str)661{662static FILE *logfile = NULL;663664if (logfile == NULL)665{666if ((logfile = fopen("PLANARITY.LOG", WRITETEXT)) == NULL)667return;668}669670if (Str != NULL)671{672fprintf(logfile, "%s", Str);673fflush(logfile);674}675else676fclose(logfile);677}678679void _LogLine(char *Str)680{681_Log(Str);682_Log("\n");683}684685static char LogStr[512];686687char *_MakeLogStr1(char *format, int one)688{689sprintf(LogStr, format, one);690return LogStr;691}692693char *_MakeLogStr2(char *format, int one, int two)694{695sprintf(LogStr, format, one, two);696return LogStr;697}698699char *_MakeLogStr3(char *format, int one, int two, int three)700{701sprintf(LogStr, format, one, two, three);702return LogStr;703}704705char *_MakeLogStr4(char *format, int one, int two, int three, int four)706{707sprintf(LogStr, format, one, two, three, four);708return LogStr;709}710711char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five)712{713sprintf(LogStr, format, one, two, three, four, five);714return LogStr;715}716717718