Graphviz 14.1.5~dev.20260324.0339
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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.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 assert(h > 0);
373 const int type = rand() % 2;
374
375 unsigned size = 0;
376 unsigned depth = 0;
377 while (size <= h) {
378 size += 1u << depth;
379 depth++;
380 }
381 depth--;
382 if (size > h) {
383 size -= 1u << depth;
384 depth--;
385 }
386
387 if (type)
388 makeBinaryTree (depth, ef);
389 else
390 makePath (size, ef);
391
392 for (unsigned i = 3; i <= size; i++) {
393 for (unsigned j = 1; j + 1 < i; j++) {
394 const unsigned th = (unsigned)rand() % (size * size);
395 if ((th <= w * w && (i < 5 || (i + 4 > h && j + 4 > h))) || th <= w)
396 ef(j,i);
397 }
398 }
399}
400
401void makeMobius(unsigned w, unsigned h, edgefn ef) {
402 if (h == 1) {
403 fprintf(stderr, "Warning: degenerate Moebius strip of %u vertices\n", w);
404 makePath(w, ef);
405 return;
406 }
407 if (w == 1) {
408 fprintf(stderr, "Warning: degenerate Moebius strip of %u vertices\n", h);
409 makePath(h, ef);
410 return;
411 }
412
413 for (unsigned i = 0; i + 1 < w; i++) {
414 for (unsigned j = 1; j < h; j++){
415 ef(j + i*h, j + (i+1)*h);
416 ef(j + i*h, j+1 + i*h);
417 }
418 }
419
420 for (unsigned i = 1; i < h; i++){
421 ef (i + (w-1)*h, i+1 + (w-1)*h);
422 }
423 for (unsigned i=1; i < w; i++) {
424 ef(i*h , (i+1)*h);
425 ef(i*h, (w-i)*h+1);
426 }
427
428 ef(1,w*h);
429}
430
431typedef struct {
432 unsigned j, d;
433} pair;
434
435typedef struct {
436 unsigned top, root;
437 unsigned* p;
438} tree_t;
439
440static tree_t *mkTree(unsigned sz) {
441 tree_t* tp = gv_alloc(sizeof(tree_t));
442 tp->root = 0;
443 tp->top = 0;
444 tp->p = gv_calloc(sz, sizeof(unsigned));
445 return tp;
446}
447
448static void
450{
451 free (tp->p);
452 free (tp);
453}
454
455static void
457{
458 tp->root = 0;
459 tp->top = 0;
460}
461
462static unsigned treeRoot(tree_t* tp) {
463 return tp->root;
464}
465
466static unsigned prevRoot(tree_t *tp) {
467 return tp->p[tp->root];
468}
469
470static unsigned treeSize(tree_t *tp) {
471 return tp->top - tp->root + 1;
472}
473
474static unsigned treeTop(tree_t *tp) {
475 return tp->top;
476}
477
478static void
480{
481 tp->root = prevRoot(tp);
482}
483
484static void addTree(tree_t *tp, unsigned sz) {
485 tp->p[tp->top+1] = tp->root;
486 tp->root = tp->top+1;
487 tp->top += sz;
488 if (sz > 1) tp->p[tp->top] = tp->top-1;
489}
490
491static void treeDup(tree_t *tp, unsigned J) {
492 unsigned M = treeSize(tp);
493 unsigned L = treeRoot(tp);
494 unsigned LL = prevRoot(tp);
495 unsigned LS = L + (J-1)*M - 1;
496 for (unsigned i = L; i <= LS; i++) {
497 if ((i-L)%M == 0) tp->p[i+M] = LL;
498 else tp->p[i+M] = tp->p[i] + M;
499 }
500 tp->top = LS + M;
501}
502
503/*****************/
504
505typedef LIST(unsigned) int_stack_t;
506
507static void push(int_stack_t *sp, unsigned j, unsigned d) {
508 LIST_PUSH_BACK(sp, j);
509 LIST_PUSH_BACK(sp, d);
510}
511
512static pair pop(int_stack_t *sp) {
513
514 // extract ints in the opposite order in which they were pushed
515 const unsigned d = LIST_POP_BACK(sp);
516 const unsigned j = LIST_POP_BACK(sp);
517
518 return (pair){j, d};
519}
520
521/*****************/
522
524static uint64_t umul(uint64_t a, uint64_t b) {
525 uint64_t res;
526 if (u64mul_overflow(a, b, &res)) {
527 fprintf(stderr, "integer overflow in %" PRIu64 " * %" PRIu64 "\n", a, b);
528 graphviz_exit(EXIT_FAILURE);
529 }
530 return res;
531}
532
534static uint64_t uadd(uint64_t a, uint64_t b) {
535 uint64_t res;
536 if (u64add_overflow(a, b, &res)) {
537 fprintf(stderr, "integer overflow in %" PRIu64 " + %" PRIu64 "\n", a, b);
538 graphviz_exit(EXIT_FAILURE);
539 }
540 return res;
541}
542
543static uint64_t *genCnt(unsigned NN) {
544 uint64_t *T = gv_calloc(NN + 1, sizeof(uint64_t));
545 unsigned NLAST = 1;
546 T[1] = 1;
547 while (NN > NLAST) {
548 uint64_t SUM = 0;
549 for (unsigned D = 1; D <= NLAST; D++) {
550 unsigned I = NLAST + 1;
551 const uint64_t TD = umul(T[D], D);
552 for (unsigned J = 1; J <= NLAST; J++) {
553 if (I <= D) break;
554 I = I-D;
555 SUM = uadd(SUM, umul(T[I], TD));
556 }
557 }
558 NLAST++;
559 T[NLAST] = SUM/(NLAST-1);
560 }
561 return T;
562}
563
564static void genTree(unsigned NN, uint64_t *T, int_stack_t *stack,
565 tree_t *TREE) {
566 pair p;
567 unsigned J;
568
569 unsigned N = NN;
570
571 while (1) {
572 while (N > 2) {
573 const uint64_t v = umul(N - 1, T[N]);
574 uint64_t Z = gv_random_u64(v);
575 unsigned D = 0;
576 bool more = true;
577 unsigned M;
578 do {
579 D++;
580 const uint64_t TD = umul(D, T[D]);
581 M = N;
582 J = 0;
583 do {
584 J++;
585 if (M < D + 1) break;
586 M -= D;
587 if (Z < umul(T[M], TD)) {
588 more = false;
589 break;
590 }
591 Z -= umul(T[M], TD);
592 } while (true);
593 } while (more);
594 push(stack, J, D);
595 N = M;
596 }
597 addTree (TREE, N);
598
599 while (1) {
600 p = pop(stack);
601 N = p.d;
602 if (N != 0) {
603 push(stack,p.j,0);
604 break;
605 }
606 J = p.j;
607 if (J > 1) treeDup (TREE, J);
608 if (treeTop(TREE) == NN) return;
609 treePop(TREE);
610 }
611 }
612
613}
614
615static void
617{
618 for (unsigned i = 2; i <= tp->top; i++)
619 ef (tp->p[i], i);
620}
621
622struct treegen_s {
623 unsigned N;
624 uint64_t *T;
625 int_stack_t sp;
627};
628
630 treegen_t* tg = gv_alloc(sizeof(treegen_t));
631
632 tg->N = N;
633 tg->T = genCnt(N);
634 tg->sp = (int_stack_t){0};
635 tg->tp = mkTree(N+1);
636
637 return tg;
638}
639
641{
642 LIST_CLEAR(&tg->sp);
643 resetTree(tg->tp);
644 genTree(tg->N, tg->T, &tg->sp, tg->tp);
645 writeTree (tg->tp, ef);
646}
647
648void
650{
651 free (tg->T);
652 LIST_FREE(&tg->sp);
653 freeTree (tg->tp);
654 free (tg);
655}
656
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:222
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:34
#define D
Definition hierarchy.c:122
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:76
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:97
static bool u64add_overflow(uint64_t a, uint64_t b, uint64_t *res)
Definition overflow.h:73
uint64_t gv_random_u64(uint64_t bound)
Definition random.c:61
random number generation
#define M
Definition randomkit.c:92
unsigned j
unsigned d
unsigned top
unsigned root
unsigned * p
uint64_t * T
int_stack_t sp
static clock_t T
Definition timing.c:19
void(* edgefn)(Agraph_t *, Agedge_t *, glCompColor)