]> granicus.if.org Git - graphviz/commitdiff
Add abstract graph generator
authorerg <devnull@localhost>
Fri, 29 Aug 2008 18:34:20 +0000 (18:34 +0000)
committererg <devnull@localhost>
Fri, 29 Aug 2008 18:34:20 +0000 (18:34 +0000)
cmd/tools/Makefile.am
cmd/tools/Makefile.old
cmd/tools/graph_generator.c [new file with mode: 0644]
cmd/tools/graph_generator.h [new file with mode: 0644]
cmd/tools/gvgen.1 [new file with mode: 0644]
cmd/tools/gvgen.c [new file with mode: 0644]

index bcfcfef9da811d7ae24a34b2dfd7eb44c1b797c6..26e60200972ff047d97ff9a230e9b62a6f8bb157 100644 (file)
@@ -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
index 4a008dcd075bf26644be5693c98ff815ee7473c0..10e75b3ee56dfcf44b53df3688dc60d43115953a 100644 (file)
@@ -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 (file)
index 0000000..b713ebe
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <graph_generator.h>
+
+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 (file)
index 0000000..66d2076
--- /dev/null
@@ -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 (file)
index 0000000..2e9ff40
--- /dev/null
@@ -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 <erg@research.att.com>
+.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 (file)
index 0000000..38ba82c
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#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<n>         : cycle \n\
+ -C<x,y>       : cylinder \n\
+ -g[f]<h,w>    : grid (folded if f is used)\n\
+ -G[f]<h,w>    : partial grid (folded if f is used)\n\
+ -h<x>         : hypercube \n\
+ -k<x>         : complete \n\
+ -o<outfile>   : put output in <outfile> (stdout)\n\
+ -p<x>         : path \n\
+ -s<x>         : star\n\
+ -S<x>         : sierpinski\n\
+ -t<x>         : binary tree \n\
+ -T<x,y>       : torus \n\
+ -w<x>         : 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);
+}