Graphviz 14.0.5~dev.20251117.1017
Loading...
Searching...
No Matches
graph_generator.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12#include <assert.h>
13#include <inttypes.h>
14#include <limits.h>
15#include <stdbool.h>
16#include <stdio.h>
17#include <stdint.h>
18#include <stdlib.h>
19#include <math.h>
20#include <graph_generator.h>
21#include <util/alloc.h>
22#include <util/exit.h>
23#include <util/list.h>
24#include <util/random.h>
25#include <util/overflow.h>
26
27void makePath(unsigned n, edgefn ef){
28 if (n == 1) {
29 ef (1, 0);
30 return;
31 }
32 for (unsigned i = 2; i <= n; i++)
33 ef (i - 1, i);
34}
35
36void makeComplete(unsigned n, edgefn ef) {
37 if (n == 1) {
38 ef (1, 0);
39 return;
40 }
41 for (unsigned i = 1; i < n; i++) {
42 for (unsigned j = i + 1; j <= n; j++) {
43 ef ( i, j);
44 }
45 }
46}
47
48void makeCircle(unsigned n, edgefn ef) {
49 if (n < 3) {
50 fprintf(stderr, "Warning: degenerate circle of %u vertices\n", n);
51 makePath(n, ef);
52 return;
53 }
54
55 for (unsigned i = 1; i < n; i++)
56 ef ( i, i + 1);
57 ef (1, n);
58}
59
60void makeStar(unsigned n, edgefn ef) {
61 if (n < 3) {
62 fprintf(stderr, "Warning: degenerate star of %u vertices\n", n);
63 makePath(n, ef);
64 return;
65 }
66
67 for (unsigned i = 2; i <= n; i++)
68 ef (1, i);
69}
70
71void makeWheel(unsigned n, edgefn ef) {
72 if (n < 4) {
73 fprintf(stderr, "Warning: degenerate wheel of %u vertices\n", n);
74 makeComplete(n, ef);
75 return;
76 }
77
78 makeStar(n, ef);
79
80 for (unsigned i = 2; i < n; i++)
81 ef( i, i + 1);
82 ef (2, n);
83}
84
85void makeCompleteB(unsigned dim1, unsigned dim2, edgefn ef) {
86 for (unsigned i = 1; i <= dim1; i++) {
87 for (unsigned j = 1; j <= dim2; j++) {
88 ef ( i, dim1 + j);
89 }
90 }
91}
92
93void makeTorus(unsigned dim1, unsigned dim2, edgefn ef) {
94 for (unsigned i = 1, n = 0; i <= dim1; i++) {
95 for (unsigned j = 1; j < dim2; j++) {
96 ef( n + j, n + j + 1);
97 }
98 ef( n + 1, n + dim2);
99 n += dim2;
100 }
101
102 for (unsigned i = 1; i <= dim2; i++) {
103 for (unsigned j = 1; j < dim1; j++) {
104 ef( dim2 * (j - 1) + i, dim2 * j + i);
105 }
106 ef( i, dim2 * (dim1 - 1) + i);
107 }
108}
109
110void makeTwistedTorus(unsigned dim1, unsigned dim2, unsigned t1, unsigned t2,
111 edgefn ef) {
112 for (unsigned i = 0; i < dim1; i++) {
113 for (unsigned j = 0; j < dim2; j++) {
114 unsigned li = (i + t1) % dim1;
115 unsigned lj = (j + 1) % dim2;
116 ef (i+j*dim1+1, li+lj*dim1+1);
117
118 li = (i + 1) % dim1;
119 lj = (j + t2) % dim2;
120 ef(i+j*dim1+1, li+lj*dim1+1);
121 }
122 }
123}
124
125void makeCylinder(unsigned dim1, unsigned dim2, edgefn ef) {
126 for (unsigned i = 1, n = 0; i <= dim1; i++) {
127 for (unsigned j = 1; j < dim2; j++) {
128 ef( n + j, n + j + 1);
129 }
130 ef( n + 1, n + dim2);
131 n += dim2;
132 }
133
134 for (unsigned i = 1; i <= dim2; i++) {
135 for (unsigned j = 1; j < dim1; j++) {
136 ef( dim2 * (j - 1) + i, dim2 * j + i);
137 }
138 }
139}
140
141#define OUTE(h) if (tl < (hd=(h))) ef( tl, hd)
142
143void makeSquareGrid(unsigned dim1, unsigned dim2, int connect_corners, int partial, edgefn ef)
144{
145 for (unsigned i = 0; i < dim1; i++)
146 for (unsigned j = 0; j < dim2; j++) {
147 // write the neighbors of the node i*dim2+j+1
148 const unsigned tl = i * dim2 + j + 1;
149 unsigned hd;
150 if (j + 1 < dim2
151 && (!partial || j < 2 * dim2 / 6 || j >= 4 * dim2 / 6
152 || i <= 2 * dim1 / 6 || i > 4 * dim1 / 6)) {
153 ef(tl, i * dim2 + j + 2);
154 }
155 if (i + 1 < dim1) {
156 ef(tl, (i + 1) * dim2 + j + 1);
157 }
158 if (connect_corners == 1) {
159 if (i == 0 && j == 0) { // upper left
160 OUTE((dim1 - 1) * dim2 + dim2);
161 } else if (i + 1 == dim1 && j == 0) { // lower left
162 OUTE(dim2);
163 } else if (i == 0 && j + 1 == dim2) { // upper right
164 OUTE((dim1 - 1) * dim2 + 1);
165 } else if (i + 1 == dim1 && j + 1 == dim2) { // lower right
166 OUTE(1);
167 }
168 } else if (connect_corners == 2) {
169 if (i == 0 && j == 0) { // upper left
170 OUTE(dim2);
171 } else if (i + 1 == dim1 && j == 0) { // lower left
172 OUTE((dim1 - 1) * dim2 + dim2);
173 } else if (i == 0 && j + 1 == dim2) { // upper right
174 OUTE(1);
175 } else if (i + 1 == dim1 && j + 1 == dim2) { // lower right
176 OUTE((dim1 - 1) * dim2 + 1);
177 }
178 }
179 }
180}
181
182void makeTree(unsigned depth, unsigned nary, edgefn ef) {
183 const double n = (pow(nary, depth) - 1) / (nary - 1); // no. of non-leaf nodes
184 unsigned idx = 2;
185
186 for (unsigned i = 1; i <= n; i++) {
187 for (unsigned j = 0; j < nary; j++) {
188 ef (i, idx++);
189 }
190 }
191}
192
193void makeBinaryTree(unsigned depth, edgefn ef) {
194 assert(depth < sizeof(unsigned) * CHAR_BIT);
195 const unsigned n = (1u << depth) - 1;
196
197 for (unsigned i = 1; i <= n; i++) {
198 ef( i, 2 * i);
199 ef( i, 2 * i + 1);
200 }
201}
202
203typedef LIST(unsigned) vtx_data;
204
206static void free_vtx(vtx_data data) {
207 LIST_FREE(&data);
208}
209
210typedef LIST(vtx_data) vtx_datas_t;
211
213static void append(vtx_datas_t *graph, size_t index, unsigned ref) {
214 assert(graph != NULL);
215
216 // expand the list as necessary
217 while (index >= LIST_SIZE(graph)) {
219 }
220
221 LIST_APPEND(LIST_AT(graph, index), ref);
222}
223
224static void constructSierpinski(unsigned v1, unsigned v2, unsigned v3,
225 unsigned depth, vtx_datas_t *graph) {
226 static unsigned last_used_node_name = 3;
227
228 if (depth > 0) {
229 const unsigned v4 = ++last_used_node_name;
230 const unsigned v5 = ++last_used_node_name;
231 const unsigned v6 = ++last_used_node_name;
232 constructSierpinski(v1, v4, v5, depth - 1, graph);
233 constructSierpinski(v2, v5, v6, depth - 1, graph);
234 constructSierpinski(v3, v4, v6, depth - 1, graph);
235 return;
236 }
237 // depth==0, Construct graph:
238
239 append(graph, v1, v2);
240 append(graph, v1, v3);
241
242 append(graph, v2, v1);
243 append(graph, v2, v3);
244
245 append(graph, v3, v1);
246 append(graph, v3, v2);
247}
248
249void makeSierpinski(unsigned depth, edgefn ef) {
250 vtx_datas_t graph = {.dtor = free_vtx};
251
252 depth--;
253 constructSierpinski(1, 2, 3, depth, &graph);
254
255 for (unsigned i = 1; i < LIST_SIZE(&graph); i++) {
256 const vtx_data *const g = LIST_AT(&graph, i);
257 // write the neighbors of the node i
258 for (size_t j = 0; j < LIST_SIZE(g); j++) {
259 const unsigned nghbr = LIST_GET(g, j);
260 if (i < nghbr) ef( i, nghbr);
261 }
262 }
263
265}
266
267static void constructTetrix(unsigned v1, unsigned v2, unsigned v3, unsigned v4,
268 unsigned depth, vtx_datas_t *graph) {
269 static unsigned last_used_node_name = 4;
270
271 if (depth > 0) {
272 const unsigned v5 = ++last_used_node_name;
273 const unsigned v6 = ++last_used_node_name;
274 const unsigned v7 = ++last_used_node_name;
275 const unsigned v8 = ++last_used_node_name;
276 const unsigned v9 = ++last_used_node_name;
277 const unsigned v10 = ++last_used_node_name;
278 constructTetrix(v1, v5, v6, v8, depth - 1, graph);
279 constructTetrix(v2, v6, v7, v9, depth - 1, graph);
280 constructTetrix(v3, v5, v7, v10, depth - 1, graph);
281 constructTetrix(v4, v8, v9, v10, depth - 1, graph);
282 return;
283 }
284 // depth==0, Construct graph:
285 append(graph, v1, v2);
286 append(graph, v1, v3);
287 append(graph, v1, v4);
288
289 append(graph, v2, v1);
290 append(graph, v2, v3);
291 append(graph, v2, v4);
292
293 append(graph, v3, v1);
294 append(graph, v3, v2);
295 append(graph, v3, v4);
296
297 append(graph, v4, v1);
298 append(graph, v4, v2);
299 append(graph, v4, v3);
300}
301
302void makeTetrix(unsigned depth, edgefn ef) {
303 vtx_datas_t graph = {.dtor = free_vtx};
304
305 depth--;
306 constructTetrix(1, 2, 3, 4, depth, &graph);
307
308 for (unsigned i = 1; i < LIST_SIZE(&graph); i++) {
309 const vtx_data *const g = LIST_AT(&graph, i);
310 // write the neighbors of the node i
311 for (size_t j = 0; j < LIST_SIZE(g); j++) {
312 const unsigned nghbr = LIST_GET(g, j);
313 if (i < nghbr) ef( i, nghbr);
314 }
315 }
316
318}
319
320void makeHypercube(unsigned dim, edgefn ef) {
321 assert(dim < sizeof(unsigned) * CHAR_BIT);
322 const unsigned n = 1u << dim;
323
324 for (unsigned i = 0; i < n; i++) {
325 for (unsigned j = 0; j < dim; j++) {
326 const unsigned neighbor = (i ^ (1u << j)) + 1;
327 if (i < neighbor)
328 ef( i + 1, neighbor);
329 }
330 }
331}
332
333void makeTriMesh(unsigned sz, edgefn ef) {
334 if (sz == 1) {
335 ef (1, 0);
336 return;
337 }
338 ef(1,2);
339 ef(1,3);
340 unsigned idx = 2;
341 for (unsigned i = 2; i < sz; i++) {
342 for (unsigned j = 1; j <= i; j++) {
343 ef(idx,idx+i);
344 ef(idx,idx+i+1);
345 if (j < i)
346 ef(idx,idx+1);
347 idx++;
348 }
349 }
350 for (unsigned j = 1; j < sz; j++) {
351 ef (idx,idx+1);
352 idx++;
353 }
354}
355
356void makeBall(unsigned w, unsigned h, edgefn ef) {
357 makeCylinder (w, h, ef);
358
359 for (unsigned i = 1; i <= h; i++)
360 ef (0, i);
361
362 const unsigned cap = w * h + 1;
363 for (unsigned i = (w - 1) * h + 1; i <= w * h; i++)
364 ef (i, cap);
365
366}
367
368/* makeRandom:
369 * No. of nodes is largest 2^n - 1 less than or equal to h.
370 */
371void makeRandom(unsigned h, unsigned w, edgefn ef) {
372 const int type = rand() % 2;
373
374 unsigned size = 0;
375 unsigned depth = 0;
376 while (size <= h) {
377 size += 1u << depth;
378 depth++;
379 }
380 depth--;
381 if (size > h) {
382 size -= 1u << depth;
383 depth--;
384 }
385
386 if (type)
387 makeBinaryTree (depth, ef);
388 else
389 makePath (size, ef);
390
391 for (unsigned i = 3; i <= size; i++) {
392 for (unsigned j = 1; j + 1 < i; j++) {
393 const unsigned th = (unsigned)rand() % (size * size);
394 if ((th <= w * w && (i < 5 || (i + 4 > h && j + 4 > h))) || th <= w)
395 ef(j,i);
396 }
397 }
398}
399
400void makeMobius(unsigned w, unsigned h, edgefn ef) {
401 if (h == 1) {
402 fprintf(stderr, "Warning: degenerate Moebius strip of %u vertices\n", w);
403 makePath(w, ef);
404 return;
405 }
406 if (w == 1) {
407 fprintf(stderr, "Warning: degenerate Moebius strip of %u vertices\n", h);
408 makePath(h, ef);
409 return;
410 }
411
412 for (unsigned i = 0; i + 1 < w; i++) {
413 for (unsigned j = 1; j < h; j++){
414 ef(j + i*h, j + (i+1)*h);
415 ef(j + i*h, j+1 + i*h);
416 }
417 }
418
419 for (unsigned i = 1; i < h; i++){
420 ef (i + (w-1)*h, i+1 + (w-1)*h);
421 }
422 for (unsigned i=1; i < w; i++) {
423 ef(i*h , (i+1)*h);
424 ef(i*h, (w-i)*h+1);
425 }
426
427 ef(1,w*h);
428}
429
430typedef struct {
431 unsigned j, d;
432} pair;
433
434typedef struct {
435 unsigned top, root;
436 unsigned* p;
437} tree_t;
438
439static tree_t *mkTree(unsigned sz) {
440 tree_t* tp = gv_alloc(sizeof(tree_t));
441 tp->root = 0;
442 tp->top = 0;
443 tp->p = gv_calloc(sz, sizeof(unsigned));
444 return tp;
445}
446
447static void
449{
450 free (tp->p);
451 free (tp);
452}
453
454static void
456{
457 tp->root = 0;
458 tp->top = 0;
459}
460
461static unsigned treeRoot(tree_t* tp) {
462 return tp->root;
463}
464
465static unsigned prevRoot(tree_t *tp) {
466 return tp->p[tp->root];
467}
468
469static unsigned treeSize(tree_t *tp) {
470 return tp->top - tp->root + 1;
471}
472
473static unsigned treeTop(tree_t *tp) {
474 return tp->top;
475}
476
477static void
479{
480 tp->root = prevRoot(tp);
481}
482
483static void addTree(tree_t *tp, unsigned sz) {
484 tp->p[tp->top+1] = tp->root;
485 tp->root = tp->top+1;
486 tp->top += sz;
487 if (sz > 1) tp->p[tp->top] = tp->top-1;
488}
489
490static void treeDup(tree_t *tp, unsigned J) {
491 unsigned M = treeSize(tp);
492 unsigned L = treeRoot(tp);
493 unsigned LL = prevRoot(tp);
494 unsigned LS = L + (J-1)*M - 1;
495 for (unsigned i = L; i <= LS; i++) {
496 if ((i-L)%M == 0) tp->p[i+M] = LL;
497 else tp->p[i+M] = tp->p[i] + M;
498 }
499 tp->top = LS + M;
500}
501
502/*****************/
503
504typedef LIST(unsigned) int_stack_t;
505
506static void push(int_stack_t *sp, unsigned j, unsigned d) {
507 LIST_PUSH_BACK(sp, j);
508 LIST_PUSH_BACK(sp, d);
509}
510
511static pair pop(int_stack_t *sp) {
512
513 // extract ints in the opposite order in which they were pushed
514 const unsigned d = LIST_POP_BACK(sp);
515 const unsigned j = LIST_POP_BACK(sp);
516
517 return (pair){j, d};
518}
519
520/*****************/
521
523static uint64_t umul(uint64_t a, uint64_t b) {
524 uint64_t res;
525 if (u64mul_overflow(a, b, &res)) {
526 fprintf(stderr, "integer overflow in %" PRIu64 " * %" PRIu64 "\n", a, b);
527 graphviz_exit(EXIT_FAILURE);
528 }
529 return res;
530}
531
533static uint64_t uadd(uint64_t a, uint64_t b) {
534 uint64_t res;
535 if (u64add_overflow(a, b, &res)) {
536 fprintf(stderr, "integer overflow in %" PRIu64 " + %" PRIu64 "\n", a, b);
537 graphviz_exit(EXIT_FAILURE);
538 }
539 return res;
540}
541
542static uint64_t *genCnt(unsigned NN) {
543 uint64_t *T = gv_calloc(NN + 1, sizeof(uint64_t));
544 unsigned NLAST = 1;
545 T[1] = 1;
546 while (NN > NLAST) {
547 uint64_t SUM = 0;
548 for (unsigned D = 1; D <= NLAST; D++) {
549 unsigned I = NLAST + 1;
550 const uint64_t TD = umul(T[D], D);
551 for (unsigned J = 1; J <= NLAST; J++) {
552 if (I <= D) break;
553 I = I-D;
554 SUM = uadd(SUM, umul(T[I], TD));
555 }
556 }
557 NLAST++;
558 T[NLAST] = SUM/(NLAST-1);
559 }
560 return T;
561}
562
563static void genTree(unsigned NN, uint64_t *T, int_stack_t *stack,
564 tree_t *TREE) {
565 pair p;
566 unsigned J;
567
568 unsigned N = NN;
569
570 while (1) {
571 while (N > 2) {
572 const uint64_t v = umul(N - 1, T[N]);
573 uint64_t Z = gv_random_u64(v);
574 unsigned D = 0;
575 bool more = true;
576 unsigned M;
577 do {
578 D++;
579 const uint64_t TD = umul(D, T[D]);
580 M = N;
581 J = 0;
582 do {
583 J++;
584 if (M < D + 1) break;
585 M -= D;
586 if (Z < umul(T[M], TD)) {
587 more = false;
588 break;
589 }
590 Z -= umul(T[M], TD);
591 } while (true);
592 } while (more);
593 push(stack, J, D);
594 N = M;
595 }
596 addTree (TREE, N);
597
598 while (1) {
599 p = pop(stack);
600 N = p.d;
601 if (N != 0) {
602 push(stack,p.j,0);
603 break;
604 }
605 J = p.j;
606 if (J > 1) treeDup (TREE, J);
607 if (treeTop(TREE) == NN) return;
608 treePop(TREE);
609 }
610 }
611
612}
613
614static void
616{
617 for (unsigned i = 2; i <= tp->top; i++)
618 ef (tp->p[i], i);
619}
620
621struct treegen_s {
622 unsigned N;
623 uint64_t *T;
624 int_stack_t sp;
626};
627
629 treegen_t* tg = gv_alloc(sizeof(treegen_t));
630
631 tg->N = N;
632 tg->T = genCnt(N);
633 tg->sp = (int_stack_t){0};
634 tg->tp = mkTree(N+1);
635
636 return tg;
637}
638
640{
641 LIST_CLEAR(&tg->sp);
642 resetTree(tg->tp);
643 genTree(tg->N, tg->T, &tg->sp, tg->tp);
644 writeTree (tg->tp, ef);
645}
646
647void
649{
650 free (tg->T);
651 LIST_FREE(&tg->sp);
652 freeTree (tg->tp);
653 free (tg);
654}
655
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define N(n)
Definition bcomps.c:58
static Agnode_t * pop(void)
Definition ccomps.c:220
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
expr procedure type
Definition exparse.y:208
#define I
Definition expr.h:71
static attrs_t * L
Definition gmlparse.c:94
void free(void *)
static gstack_t * push(gstack_t *s, Agraph_t *subg)
Definition grammar.c:1655
node NULL
Definition grammar.y:181
static void treePop(tree_t *tp)
static uint64_t * genCnt(unsigned NN)
void makeTriMesh(unsigned sz, edgefn ef)
static unsigned prevRoot(tree_t *tp)
void makeHypercube(unsigned dim, edgefn ef)
static void addTree(tree_t *tp, unsigned sz)
static unsigned treeSize(tree_t *tp)
void makeCircle(unsigned n, edgefn ef)
void makeTree(unsigned depth, unsigned nary, edgefn ef)
static void constructSierpinski(unsigned v1, unsigned v2, unsigned v3, unsigned depth, vtx_datas_t *graph)
static uint64_t umul(uint64_t a, uint64_t b)
multiply two 64-bit unsigned integers, exiting on overflow
static void writeTree(tree_t *tp, edgefn ef)
void makeWheel(unsigned n, edgefn ef)
void makeSquareGrid(unsigned dim1, unsigned dim2, int connect_corners, int partial, edgefn ef)
void makeBinaryTree(unsigned depth, edgefn ef)
static void constructTetrix(unsigned v1, unsigned v2, unsigned v3, unsigned v4, unsigned depth, vtx_datas_t *graph)
void makeComplete(unsigned n, edgefn ef)
void makeMobius(unsigned w, unsigned h, edgefn ef)
treegen_t * makeTreeGen(unsigned N)
void makeCompleteB(unsigned dim1, unsigned dim2, edgefn ef)
void makeBall(unsigned w, unsigned h, edgefn ef)
static void genTree(unsigned NN, uint64_t *T, int_stack_t *stack, tree_t *TREE)
void makePath(unsigned n, edgefn ef)
static unsigned treeRoot(tree_t *tp)
static void resetTree(tree_t *tp)
void makeTetrix(unsigned depth, edgefn ef)
void makeCylinder(unsigned dim1, unsigned dim2, edgefn ef)
void makeStar(unsigned n, edgefn ef)
static void treeDup(tree_t *tp, unsigned J)
void freeTreeGen(treegen_t *tg)
void makeTorus(unsigned dim1, unsigned dim2, edgefn ef)
void makeTwistedTorus(unsigned dim1, unsigned dim2, unsigned t1, unsigned t2, edgefn ef)
#define OUTE(h)
void makeRandomTree(treegen_t *tg, edgefn ef)
void makeRandom(unsigned h, unsigned w, edgefn ef)
static void freeTree(tree_t *tp)
void makeSierpinski(unsigned depth, edgefn ef)
static uint64_t uadd(uint64_t a, uint64_t b)
add two 64-bit unsigned integers, exiting on overflow
static tree_t * mkTree(unsigned sz)
static unsigned treeTop(tree_t *tp)
Agraph_t * graph(char *name)
Definition gv.cpp:32
#define D
Definition hierarchy.c:120
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:73
type-generic dynamically expanding list
#define LIST_AT(list, index)
Definition list.h:168
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_CLEAR(list)
Definition list.h:240
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_FREE(list)
Definition list.h:370
#define LIST_POP_BACK(list)
Definition list.h:407
#define LIST_PUSH_BACK(list, item)
Definition list.h:384
#define LIST_GET(list, index)
Definition list.h:155
#define neighbor(t, i, edim, elist)
Definition make_map.h:41
static const int dim
arithmetic overflow helpers
static bool u64mul_overflow(uint64_t a, uint64_t b, uint64_t *res)
Definition overflow.h:77
static bool u64add_overflow(uint64_t a, uint64_t b, uint64_t *res)
Definition overflow.h:51
uint64_t gv_random_u64(uint64_t bound)
Definition random.c:59
random number generation
#define M
Definition randomkit.c:90
unsigned j
unsigned d
unsigned top
unsigned root
unsigned * p
uint64_t * T
int_stack_t sp
static clock_t T
Definition timing.c:17
void(* edgefn)(Agraph_t *, Agedge_t *, glCompColor)