From: erg Date: Fri, 29 Aug 2008 18:34:20 +0000 (+0000) Subject: Add abstract graph generator X-Git-Tag: LAST_LIBGRAPH~32^2~3508 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2e7b2d2a86058e91ea15aa46d374b52445db9325;p=graphviz Add abstract graph generator --- diff --git a/cmd/tools/Makefile.am b/cmd/tools/Makefile.am index bcfcfef9d..26e602009 100644 --- a/cmd/tools/Makefile.am +++ b/cmd/tools/Makefile.am @@ -16,15 +16,15 @@ AM_CPPFLAGS = \ pdfdir = $(pkgdatadir)/doc/pdf -noinst_HEADERS = colortbl.h convert.h mmio.h matrix_market.h +noinst_HEADERS = colortbl.h convert.h mmio.h matrix_market.h graph_generator.h bin_PROGRAMS = gc gvcolor gxl2gv acyclic nop ccomps sccmap tred \ - unflatten gvpack dijkstra bcomps mm2gv + unflatten gvpack dijkstra bcomps mm2gv gvgen man_MANS = gc.1 gvcolor.1 gxl2gv.1 acyclic.1 nop.1 ccomps.1 sccmap.1 \ - tred.1 unflatten.1 gvpack.1 dijkstra.1 bcomps.1 mm2gv.1 + tred.1 unflatten.1 gvpack.1 dijkstra.1 bcomps.1 mm2gv.1 gvgen.1 pdf_DATA = gc.1.pdf gvcolor.1.pdf gxl2gv.1.pdf acyclic.1.pdf \ nop.1.pdf ccomps.1.pdf sccmap.1.pdf tred.1.pdf \ unflatten.1.pdf gvpack.1.pdf dijkstra.1.pdf \ - bcomps.1.pdf mm2gv.1.pdf + bcomps.1.pdf mm2gv.1.pdf gvgen.1.pdf install-data-hook: (cd $(DESTDIR)$(man1dir); rm -f gv2gxl.1; $(LN_S) gxl2gv.1 gv2gxl.1;) @@ -161,6 +161,14 @@ dijkstra_LDADD = \ dijkstra.1.pdf: $(srcdir)/dijkstra.1 - @GROFF@ -Tps -man $(srcdir)/dijkstra.1 | @PS2PDF@ - - >dijkstra.1.pdf +gvgen_SOURCES = gvgen.c graph_generator.c + +gvgen_LDADD = \ + $(top_builddir)/lib/cgraph/libcgraph.la + +gvgen.1.pdf: $(srcdir)/gvgen.1 + - @GROFF@ -Tps -man $(srcdir)/gvgen.1 | @PS2PDF@ - - >gvgen.1.pdf + EXTRA_DIST = $(man_MANS) $(pdf_DATA) Makefile.old CLEANFILES = stamp.h diff --git a/cmd/tools/Makefile.old b/cmd/tools/Makefile.old index 4a008dcd0..10e75b3ee 100644 --- a/cmd/tools/Makefile.old +++ b/cmd/tools/Makefile.old @@ -1,5 +1,5 @@ ALL_BIN = gc gvcolor acyclic nop ccomps sccmap tred unflatten gxl2dot \ - dot2gxl dijkstra gvpack bcomps + dot2gxl dijkstra gvpack bcomps mm2gv gvgen ALL = $(ALL_BIN) dot2gxl.1 all : $(ALL) @@ -66,11 +66,18 @@ SCCOBJS = sccmap.o GXLOBJS = cvtgxl.o dot2gxl.o gxl2dot.o GVPOBJS = gvpack.o gvpack_builtins.o MMGVOBJS = mm2gv.o matrix_market.o mmio.o +GVGENOBJS = gvgen.o graph_generator.o BCCOBJS = bcomps.o MANS = acyclic.1 gvcolor.1 nop.1 tred.1 ccomps.1 gc.1 sccmap.1 \ unflatten.1 gxl2dot.1 dot2gxl.1 gvpack.1 bcomps.1 +gvgen : gvgen.o + $(CSLD) $(LDFLAGS) gvgen.o $(ALIBS) -o $@ + +gvgen.o : gvgen.c + $(CC) -c $(CCFLAGS) $(DEFINES) $(DNINCS) $(INCS) gvgen.c + mm2gv : $(GVPOBJS) $(CPP) $(LDFLAGS) $(MMGVOBJS) $(INGLIB) $(PDNLIBS) -o $@ diff --git a/cmd/tools/graph_generator.c b/cmd/tools/graph_generator.c new file mode 100644 index 000000000..b713ebe96 --- /dev/null +++ b/cmd/tools/graph_generator.c @@ -0,0 +1,292 @@ +/* $Id$Revision: */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#include +#include +#include +#include + +void makePath(int n, edgefn ef) +{ + int i; + + if (n == 1) { + ef (1, 0); + return; + } + for (i = 2; i <= n; i++) + ef (i - 1, i); +} + +void makeComplete(int n, edgefn ef) +{ + int i, j; + + if (n == 1) { + ef (1, 0); + return; + } + for (i = 1; i < n; i++) { + for (j = i + 1; j <= n; j++) { + ef ( i, j); + } + } +} + +void makeCircle(int n, edgefn ef) +{ + int i; + + if (n < 3) { + fprintf(stderr, "Warning: degenerate circle of %d vertices\n", n); + makePath(n, ef); + return; + } + + for (i = 1; i < n; i++) + ef ( i, i + 1); + ef (1, n); +} + +void makeStar(int n, edgefn ef) +{ + int i; + + if (n < 3) { + fprintf(stderr, "Warning: degenerate star of %d vertices\n", n); + makePath(n, ef); + return; + } + + for (i = 2; i <= n; i++) + ef (1, i); +} + +void makeWheel(int n, edgefn ef) +{ + int i; + + if (n < 4) { + fprintf(stderr, "Warning: degenerate wheel of %d vertices\n", n); + makeComplete(n, ef); + return; + } + + makeStar(n, ef); + + for (i = 2; i < n; i++) + ef( i, i + 1); + ef (2, n); +} + +void makeTorus(int dim1, int dim2, edgefn ef) +{ + int i, j, n = 0; + + for (i = 1; i <= dim1; i++) { + for (j = 1; j < dim2; j++) { + ef( n + j, n + j + 1); + } + ef( n + 1, n + dim2); + n += dim2; + } + + for (i = 1; i <= dim2; i++) { + for (j = 1; j < dim1; j++) { + ef( dim2 * (j - 1) + i, dim2 * j + i); + } + ef( i, dim2 * (dim1 - 1) + i); + } +} + +void makeCylinder(int dim1, int dim2, edgefn ef) +{ + int i, j, n = 0; + + for (i = 1; i <= dim1; i++) { + for (j = 1; j < dim2; j++) { + ef( n + j, n + j + 1); + } + ef( n + 1, n + dim2); + n += dim2; + } + + for (i = 1; i <= dim2; i++) { + for (j = 1; j < dim1; j++) { + ef( dim2 * (j - 1) + i, dim2 * j + i); + } + } +} + +#define OUTE(h) if (tl < (hd=(h))) ef( tl, hd) + +void +makeSquareGrid(int dim1, int dim2, int connect_corners, int partial, edgefn ef) +{ + int i, j, tl, hd; + + for (i = 0; i < dim1; i++) + for (j = 0; j < dim2; j++) { + // write the neighbors of the node i*dim2+j+1 + tl = i * dim2 + j + 1; + if (j > 0 + && (!partial || j <= 2 * dim2 / 6 || j > 4 * dim2 / 6 + || i <= 2 * dim1 / 6 || i > 4 * dim1 / 6)) { + OUTE(i * dim2 + j); + } + if (j < dim2 - 1 + && (!partial || j < 2 * dim2 / 6 || j >= 4 * dim2 / 6 + || i <= 2 * dim1 / 6 || i > 4 * dim1 / 6)) { + OUTE(i * dim2 + j + 2); + } + if (i > 0) { + OUTE((i - 1) * dim2 + j + 1); + } + if (i < dim1 - 1) { + OUTE((i + 1) * dim2 + j + 1); + } + if (connect_corners == 1) { + if (i == 0 && j == 0) { // upper left + OUTE((dim1 - 1) * dim2 + dim2); + } else if (i == (dim1 - 1) && j == 0) { // lower left + OUTE(dim2); + } else if (i == 0 && j == (dim2 - 1)) { // upper right + OUTE((dim1 - 1) * dim2 + 1); + } else if (i == (dim1 - 1) && j == (dim2 - 1)) { // lower right + OUTE(1); + } + } else if (connect_corners == 2) { + if (i == 0 && j == 0) { // upper left + OUTE(dim2); + } else if (i == (dim1 - 1) && j == 0) { // lower left + OUTE((dim1 - 1) * dim2 + dim2); + } else if (i == 0 && j == (dim2 - 1)) { // upper right + OUTE(1); + } else if (i == (dim1 - 1) && j == (dim2 - 1)) { // lower right + OUTE((dim1 - 1) * dim2 + 1); + } + } + } +} + +void makeBinaryTree(int depth, edgefn ef) +{ + unsigned int i; + unsigned int n = (1 << depth) - 1; + + for (i = 0; i < n; i++) { + ef( i + 1, 2 * i + 2); + ef( i + 1, 2 * i + 3); + } +} + +typedef struct { + int nedges; + int* edges; +} vtx_data; + +static void +constructSierpinski(int v1, int v2, int v3, int depth, vtx_data* graph) +{ + static int last_used_node_name = 3; + int v4, v5, v6; + + int nedges; + + if (depth > 0) { + v4 = ++last_used_node_name; + v5 = ++last_used_node_name; + v6 = ++last_used_node_name; + constructSierpinski(v1, v4, v5, depth - 1, graph); + constructSierpinski(v2, v5, v6, depth - 1, graph); + constructSierpinski(v3, v4, v6, depth - 1, graph); + return; + } + // depth==0, Construct graph: + + nedges = graph[v1].nedges; + graph[v1].edges[nedges++] = v2; + graph[v1].edges[nedges++] = v3; + graph[v1].nedges = nedges; + + nedges = graph[v2].nedges; + graph[v2].edges[nedges++] = v1; + graph[v2].edges[nedges++] = v3; + graph[v2].nedges = nedges; + + nedges = graph[v3].nedges; + graph[v3].edges[nedges++] = v1; + graph[v3].edges[nedges++] = v2; + graph[v3].nedges = nedges; + + return; + +} + +#define N_NEW(n,t) (t*)malloc((n)*sizeof(t)) + +void makeSierpinski(int depth, edgefn ef) +{ + vtx_data* graph; + int* edges; + int n; + int nedges; + int i, j; + + depth--; + n = 3 * (1 + ((int) (pow(3.0, (double) depth) + 0.5) - 1) / 2); + + nedges = (int) (pow(3.0, depth + 1.0) + 0.5); + + graph = N_NEW(n + 1, vtx_data); + edges = N_NEW(4 * n, int); + + for (i = 1; i <= n; i++) { + graph[i].edges = edges; + edges += 4; + graph[i].nedges = 0; + } + + constructSierpinski(1, 2, 3, depth, graph); + + for (i = 1; i <= n; i++) { + int nghbr; + // write the neighbors of the node i + for (j = 0; j < graph[i].nedges; j++) { + nghbr = graph[i].edges[j]; + if (i < nghbr) ef( i, nghbr); + } + } + + free(graph[1].edges); + free(graph); +} + +void makeHypercube(int dim, edgefn ef) +{ + int i, j, n; + int neighbor; + + n = 1 << dim; + + for (i = 0; i < n; i++) { + for (j = 0; j < dim; j++) { + neighbor = (i ^ (1 << j)) + 1; + if (i < neighbor) + ef( i + 1, neighbor); + } + } +} diff --git a/cmd/tools/graph_generator.h b/cmd/tools/graph_generator.h new file mode 100644 index 000000000..66d2076fb --- /dev/null +++ b/cmd/tools/graph_generator.h @@ -0,0 +1,34 @@ +/* $Id$Revision: */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#ifndef GRAPH_GENERATOR_H +#define GRAPH_GENERATOR_H + +typedef void (*edgefn)(int, int); + +extern void makeCircle(int , edgefn); +extern void makeComplete(int , edgefn); +extern void makePath(int , edgefn); +extern void makeStar(int , edgefn); +extern void makeWheel (int, edgefn); +extern void makeTorus(int , int , edgefn); +extern void makeCylinder(int , int , edgefn); +extern void makeSquareGrid(int , int , int, int, edgefn); +extern void makeBinaryTree(int , edgefn); +extern void makeSierpinski(int , edgefn); +extern void makeHypercube(int , edgefn); + +#endif diff --git a/cmd/tools/gvgen.1 b/cmd/tools/gvgen.1 new file mode 100644 index 000000000..2e9ff4089 --- /dev/null +++ b/cmd/tools/gvgen.1 @@ -0,0 +1,130 @@ +.TH GC 1 "27 March 2008" +.SH NAME +gvgen \- generate graphs +.SH SYNOPSIS +.B gvgen +[ +.B \-d? +] +[ +.BI -c n +] +[ +.BI -C x,y +] +[ +.BI -g [\fBf\fP]x,y +] +[ +.BI -G [\fBf\fP]x,y +] +[ +.BI -h n +] +[ +.BI -k n +] +[ +.BI -p n +] +[ +.BI -s n +] +[ +.BI -S n +] +[ +.BI -t n +] +[ +.BI -T x,y +] +[ +.BI -w n +] +[ +.BI -o outfile +] +.SH DESCRIPTION +.B gvgen +generates a variety of simple, regularly-structured abstract +graphs. +.SH OPTIONS +The following options are supported: +.TP +.BI \-c " n" +Generate a cycle with \fIn\fP vertices and edges. +.TP +.BI \-C " x,y" +Generate an \fIx\fP by \fIy\fP cylinder. +This will have \fIx*y\fP vertices and +\fI2*x*y - y\fP edges. +.TP +.BI \-g " [\fBf\fP]x,y" +Generate an \fIx\fP by \fIy\fP grid. +If \fBf\fP is given, the grid is folded, with an edge +attaching each pair of opposing corner vertices. +This will have \fIx*y\fP vertices and +\fI2*x*y - y - x\fP edges if unfolded and +\fI2*x*y - y - x + 2\fP edges if folded. +.TP +.BI \-G " [\fBf\fP]x,y" +Generate an \fIx\fP by \fIy\fP partial grid. +If \fBf\fP is given, the grid is folded, with an edge +attaching each pair of opposing corner vertices. +This will have \fIx*y\fP vertices. +.TP +.BI \-h " n" +Generate a hypercube of degree \fIn\fP. +This will have \fI2^n\fP vertices and \fIn*2^(n-1)\fP edges. +.TP +.BI \-k " n" +Generate a complete graph on \fIn\fP vertices with +\fIn*(n-1)/2\fP edges. +.TP +.BI \-p " n" +Generate a path on \fIn\fP vertices. +This will have \fIn-1\fP edges. +.TP +.BI \-s " n" +Generate a star on \fIn\fP vertices. +This will have \fIn-1\fP edges. +.TP +.BI \-S " n" +Generate a Sierpinski graph of order \fIn\fP. +This will have \fI3*(3^(n-1) - 1)/2\fP vertices and +\fI3^n\fP edges. +.TP +.BI \-t " n" +Generate a binary tree of height \fIn\fP. +This will have \fI2^n-1\fP vertices and +\fI2^n-2\fP edges. +.TP +.BI \-T " x,y" +Generate an \fIx\fP by \fIy\fP torus. +This will have \fIx*y\fP vertices and +\fI2*x*y\fP edges. +.TP +.BI \-w " n" +Generate a path on \fIn\fP vertices. +This will have \fIn-1\fP edges. +.TP +.BI \-o " outfile" +If specified, the generated graph is written into the file +.I outfile. +Otherwise, the graph is written to standard out. +.TP +.B \-d +Make the generated graph directed. +.TP +.B \-? +Print usage information. +.SH "EXIT STATUS" +.B gvgen +exits with 0 on successful completion, +and exits with 1 if given an ill-formed or incorrect flag, +or if the specified output file could not be opened. +.SH AUTHOR +Emden R. Gansner +.SH "SEE ALSO" +gc(1), acyclic(1), gvpr(1), gvcolor(1), ccomps(1), sccmap(1), tred(1), libgraph(3) diff --git a/cmd/tools/gvgen.c b/cmd/tools/gvgen.c new file mode 100644 index 000000000..38ba82c45 --- /dev/null +++ b/cmd/tools/gvgen.c @@ -0,0 +1,349 @@ +/* $Id$Revision: */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +/* + * Written by Emden Gansner + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_GETOPT_H +#include +#else +#include "compat_getopt.h" +#endif +#include "graph_generator.h" + +typedef enum { unknown, grid, circle, complete, path, tree, torus, cylinder, + sierpinski, hypercube, star, wheel +} GraphType; + +typedef struct { + int graphSize1; + int graphSize2; + int Verbose; + int isPartial; + int foldVal; + int directed; + FILE *outfile; +} opts_t; + +static char *cmd; + +static FILE *openFile(char *name, char *mode) +{ + FILE *fp; + char *modestr; + + fp = fopen(name, mode); + if (!fp) { + if (*mode == 'r') + modestr = "reading"; + else + modestr = "writing"; + fprintf(stderr, "%s: could not open file %s for %s\n", + cmd, name, modestr); + exit(1); + } + return fp; +} + +static char *Usage = "Usage: %s [-dV?] [options]\n\ + -c : cycle \n\ + -C : cylinder \n\ + -g[f] : grid (folded if f is used)\n\ + -G[f] : partial grid (folded if f is used)\n\ + -h : hypercube \n\ + -k : complete \n\ + -o : put output in (stdout)\n\ + -p : path \n\ + -s : star\n\ + -S : sierpinski\n\ + -t : binary tree \n\ + -T : torus \n\ + -w : wheel\n\ + -d : directed graph\n\ + -V : verbose mode\n\ + -? : print usage\n"; + +static void usage(int v) +{ + fprintf(v ? stderr : stdout, Usage, cmd); + exit(v); +} + +static void errexit(char opt) +{ + fprintf(stderr, "in flag -%c\n", opt); + usage(1); +} + +static int readPos(char *s, char **e, int min) +{ + int d; + + d = strtol(s, e, 10); + if (s == *e) { + fprintf(stderr, "ill-formed integer \"%s\" ", s); + return -1; + } + if (d < min) { + fprintf(stderr, "integer \"%s\" less than %d", s, min); + return -1; + } + return d; +} + +/* setOne: + * Return non-zero on error. + */ +static int setOne(char *s, opts_t* opts) +{ + int d; + char *next; + + d = readPos(s, &next, 1); + if (d > 0) { + opts->graphSize1 = d; + return 0; + } + else return d; +} + +/* setOne: + * Return non-zero on error. + */ +static int setTwo(char *s, opts_t* opts) +{ + int d; + char *next; + + d = readPos(s, &next, 1); + if (d < 0) + return d; + opts->graphSize1 = d; + + if (*next != ',') { + fprintf(stderr, "ill-formed int pair \"%s\" ", s); + return -1; + } + + s = next + 1; + d = readPos(s, &next, 1); + if (d > 1) { + opts->graphSize2 = d; + return 0; + } + else return d; +} + +static char* setFold(char *s, opts_t* opts) +{ + char *next; + + if (*s == 'f') { + next = s+1; + opts->foldVal = 1; + } + else + next = s; + + return next; +} + +static char *optList = ":c:C:dg:G:h:k:o:p:s:S:t:T:Vw:?"; + +static GraphType init(int argc, char *argv[], opts_t* opts) +{ + int c; + GraphType graphType = unknown; + + cmd = argv[0]; + while ((c = getopt(argc, argv, optList)) != -1) { + switch (c) { + case 'c': + graphType = circle; + if (setOne(optarg, opts)) + errexit(c); + break; + case 'C': + graphType = cylinder; + if (setTwo(optarg, opts)) + errexit(c); + break; + case 'd': + opts->directed = 1; + break; + case 'G': + opts->isPartial = 1; + case 'g': + graphType = grid; + optarg = setFold (optarg, opts); + if (setTwo(optarg, opts)) + errexit(c); + break; + case 'h': + graphType = hypercube; + if (setOne(optarg, opts)) + errexit(c); + break; + case 'k': + graphType = complete; + if (setOne(optarg, opts)) + errexit(c); + break; + case 'o': + opts->outfile = openFile(optarg, "w"); + break; + case 'p': + graphType = path; + if (setOne(optarg, opts)) + errexit(c); + break; + case 'S': + graphType = sierpinski; + if (setOne(optarg, opts)) + errexit(c); + break; + case 's': + graphType = star; + if (setOne(optarg, opts)) + errexit(c); + break; + case 't': + graphType = tree; + if (setOne(optarg, opts)) + errexit(c); + break; + case 'T': + graphType = torus; + if (setTwo(optarg, opts)) + errexit(c); + break; + case 'V': + opts->Verbose = 1; + break; + case 'w': + graphType = wheel; + if (setOne(optarg, opts)) + errexit(c); + break; + case '?': + if (optopt == '?') + usage(0); + else + fprintf(stderr, "Unrecognized flag \"-%c\" - ignored\n", + optopt); + break; + } + } + + argc -= optind; + argv += optind; + if (!opts->outfile) + opts->outfile = stdout; + if (graphType == unknown) { + fprintf(stderr, "Graph type not set\n"); + usage(1); + } + + return graphType; +} + +static opts_t opts; + +static void dirfn (int t, int h) +{ + if (h > 0) + fprintf (opts.outfile, " %d -> %d\n", t, h); + else + fprintf (opts.outfile, " %d\n", t); +} + +static void undirfn (int t, int h) +{ + if (h > 0) + fprintf (opts.outfile, " %d -- %d\n", t, h); + else + fprintf (opts.outfile, " %d\n", t); +} + +int main(int argc, char *argv[]) +{ + GraphType graphType = init(argc, argv, &opts); + edgefn ef; + + if (opts.directed) { + fprintf(opts.outfile, "digraph {\n"); + ef = dirfn; + } + else { + fprintf(opts.outfile, "graph {\n"); + ef = undirfn; + } + + switch (graphType) { + case grid: + makeSquareGrid(opts.graphSize1, opts.graphSize2, + opts.foldVal, opts.isPartial, ef); + break; + case circle: + makeCircle(opts.graphSize1, ef); + break; + case path: + makePath(opts.graphSize1, ef); + break; + case tree: + makeBinaryTree(opts.graphSize1, ef); + break; + case torus: + makeTorus(opts.graphSize1, opts.graphSize2, ef); + break; + case cylinder: + makeCylinder(opts.graphSize1, opts.graphSize2, ef); + break; + case sierpinski: + makeSierpinski(opts.graphSize1, ef); + break; + case complete: + makeComplete(opts.graphSize1, ef); + break; + case hypercube: + makeHypercube(opts.graphSize1, ef); + break; + case star: + makeStar(opts.graphSize1, ef); + break; + case wheel: + makeWheel(opts.graphSize1, ef); + break; + default: + /* can't happen */ + break; + } + fprintf(opts.outfile, "}\n"); + + exit(0); +}