Graphviz 13.1.0~dev.20250626.0830
Loading...
Searching...
No Matches
mincross.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
12/*
13 * dot_mincross(g) takes a ranked graphs, and finds an ordering
14 * that avoids edge crossings. clusters are expanded.
15 * N.B. the rank structure is global (not allocated per cluster)
16 * because mincross may compare nodes in different clusters.
17 */
18
19#include <assert.h>
20#include <cgraph/cgraph.h>
21#include <dotgen/dot.h>
22#include <inttypes.h>
23#include <limits.h>
24#include <stdbool.h>
25#include <stdint.h>
26#include <stdlib.h>
27#include <string.h>
28#include <util/alloc.h>
29#include <util/exit.h>
30#include <util/gv_math.h>
31#include <util/itos.h>
32#include <util/list.h>
33#include <util/streq.h>
34
36 size_t nrows;
37 size_t ncols;
38 uint8_t *data;
39 size_t allocated;
40};
41
48static bool matrix_get(adjmatrix_t *me, size_t row, size_t col) {
49 assert(me != NULL);
50
51 // if this index is beyond anything set, infer it as unset
52 const size_t index = row * me->ncols + col;
53 const size_t byte_index = index / 8;
54 const size_t bit_index = index % 8;
55 if (byte_index >= me->allocated) {
56 return false;
57 }
58
59 return (me->data[byte_index] >> bit_index) & 1;
60}
61
67static void matrix_set(adjmatrix_t *me, size_t row, size_t col) {
68 assert(me != NULL);
69
70 // if we are updating beyond allocated space, expand the backing store
71 const size_t index = row * me->ncols + col;
72 const size_t byte_index = index / 8;
73 const size_t bit_index = index % 8;
74 if (byte_index >= me->allocated) {
75 me->data = gv_recalloc(me->data, me->allocated, byte_index + 1,
76 sizeof(me->data[0]));
77 me->allocated = byte_index + 1;
78 }
79
80 me->data[byte_index] |= (uint8_t)(UINT8_C(1) << bit_index);
81}
82
83/* #define DEBUG */
84#define MARK(v) (ND_mark(v))
85#define saveorder(v) (ND_coord(v)).x
86#define flatindex(v) ((size_t)ND_low(v))
87
88 /* forward declarations */
89static bool medians(graph_t * g, int r0, int r1);
90static int nodeposcmpf(const void *, const void *);
91static int edgeidcmpf(const void *, const void *);
92static void flat_breakcycles(graph_t * g);
93static void flat_reorder(graph_t * g);
94static void flat_search(graph_t * g, node_t * v);
95static void init_mincross(graph_t * g);
96static void merge2(graph_t * g);
97static void init_mccomp(graph_t *g, size_t c);
98static void cleanup2(graph_t *g, int64_t nc);
100static int64_t mincross_clust(graph_t *g, ints_t *scratch);
102static int64_t mincross(graph_t *g, int startpass, ints_t *scratch);
103static void mincross_step(graph_t * g, int pass);
104static void mincross_options(graph_t * g);
105static void save_best(graph_t * g);
106static void restore_best(graph_t * g);
107static adjmatrix_t *new_matrix(size_t i, size_t j);
108static void free_matrix(adjmatrix_t * p);
109static int ordercmpf(const void *, const void *);
110static int64_t ncross(ints_t *scratch);
111#ifdef DEBUG
112void check_rs(graph_t * g, int null_ok);
113void check_order(void);
114void check_vlists(graph_t * g);
115void node_in_root_vlist(node_t * n);
116#endif
117
118
119 /* mincross parameters */
120static int MinQuit;
121static const double Convergence = .995;
122
123static graph_t *Root;
126static int *TI_list;
127static bool ReMincross;
128
129#if defined(DEBUG) && DEBUG > 1
130static void indent(graph_t* g)
131{
132 if (g->parent) {
133 fprintf (stderr, " ");
134 indent(g->parent);
135 }
136}
137
139static void nname(node_t *v, FILE *stream) {
140 if (ND_node_type(v)) {
141 if (ND_ranktype(v) == CLUSTER)
142 fprintf(stream, "v%s_%p", agnameof(ND_clust(v)), v);
143 else
144 fprintf(stream, "v_%p", v);
145 } else
146 fputs(agnameof(v), stream);
147}
148static void dumpg (graph_t* g)
149{
150 int j, i, r;
151 node_t* v;
152 edge_t* e;
153
154 fprintf (stderr, "digraph A {\n");
155 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
156 fprintf (stderr, " subgraph {rank=same ");
157 for (i = 0; i < GD_rank(g)[r].n; i++) {
158 v = GD_rank(g)[r].v[i];
159 if (i > 0)
160 fputs(" -> ", stderr);
161 nname(v, stderr);
162 }
163 if (i > 1) fprintf (stderr, " [style=invis]}\n");
164 else fprintf (stderr, " }\n");
165 }
166 for (r = GD_minrank(g); r < GD_maxrank(g); r++) {
167 for (i = 0; i < GD_rank(g)[r].n; i++) {
168 v = GD_rank(g)[r].v[i];
169 for (j = 0; (e = ND_out(v).list[j]); j++) {
170 nname(v, stderr);
171 fputs(" -> ", stderr);
172 nname(aghead(e), stderr);
173 fputc('\n', stderr);
174 }
175 }
176 }
177 fprintf (stderr, "}\n");
178}
179static void dumpr (graph_t* g, int edges)
180{
181 int j, i, r;
182 node_t* v;
183 edge_t* e;
184
185 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
186 fprintf (stderr, "[%d] ", r);
187 for (i = 0; i < GD_rank(g)[r].n; i++) {
188 v = GD_rank(g)[r].v[i];
189 nname(v, stderr);
190 fprintf(stderr, "(%.02f,%d) ", saveorder(v),ND_order(v));
191 }
192 fprintf (stderr, "\n");
193 }
194 if (edges == 0) return;
195 for (r = GD_minrank(g); r < GD_maxrank(g); r++) {
196 for (i = 0; i < GD_rank(g)[r].n; i++) {
197 v = GD_rank(g)[r].v[i];
198 for (j = 0; (e = ND_out(v).list[j]); j++) {
199 nname(v, stderr);
200 fputs(" -> ", stderr);
201 nname(aghead(e), stderr);
202 fputc('\n', stderr);
203 }
204 }
205 }
206}
207#endif
208
209typedef struct {
211 int x, lo, hi;
213} info_t;
214
215#define ND_x(n) (((info_t*)AGDATA(n))->x)
216#define ND_lo(n) (((info_t*)AGDATA(n))->lo)
217#define ND_hi(n) (((info_t*)AGDATA(n))->hi)
218#define ND_np(n) (((info_t*)AGDATA(n))->np)
219#define ND_idx(n) (ND_order(ND_np(n)))
220
221static void
223{
224 Agnode_t* n;
225 Agnode_t* nxt;
226
227 for (n = agfstnode(sg); n; n = nxt) {
228 nxt = agnxtnode (sg, n);
229 agdelnode(sg,n);
230 }
231}
232
233#define isBackedge(e) (ND_idx(aghead(e)) > ND_idx(agtail(e)))
234
235static Agnode_t*
237{
238 Agnode_t* n;
239
240 for (n = agfstnode(sg); n; n = agnxtnode(sg, n))
241 if (agdegree(g,n,1,0) == 0) return n;
242 return NULL;
243}
244
245static int
247{
248 Agnode_t* n;
249 Agedge_t* e;
250 Agedge_t* nxte;
251 int cnt = 0;
252
253 while ((n = findSource(g, sg))) {
254 arr[cnt++] = ND_np(n);
255 agdelnode(sg, n);
256 for (e = agfstout(g, n); e; e = nxte) {
257 nxte = agnxtout(g, e);
258 agdeledge(g, e);
259 }
260 }
261 return cnt;
262}
263
264static int
265getComp (graph_t* g, node_t* n, graph_t* comp, int* indices)
266{
267 int backedge = 0;
268 Agedge_t* e;
269
270 ND_x(n) = 1;
271 indices[agnnodes(comp)] = ND_idx(n);
272 agsubnode(comp, n, 1);
273 for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
274 if (isBackedge(e)) backedge++;
275 if (!ND_x(aghead(e)))
276 backedge += getComp(g, aghead(e), comp, indices);
277 }
278 for (e = agfstin(g,n); e; e = agnxtin(g,e)) {
279 if (isBackedge(e)) backedge++;
280 if (!ND_x(agtail(e)))
281 backedge += getComp(g, agtail(e), comp, indices);
282 }
283 return backedge;
284}
285
287static void
289{
290 int cnt;
291 bool haveBackedge = false;
292 Agraph_t* sg;
293 Agnode_t* n;
294 Agnode_t* nxtp;
295 Agnode_t* v;
296
297 for (n = agfstnode(g); n; n = nxtp) {
298 v = nxtp = agnxtnode(g, n);
299 for (; v; v = agnxtnode(g, v)) {
300 if (ND_hi(v) <= ND_lo(n)) {
301 haveBackedge = true;
302 agedge(g, v, n, NULL, 1);
303 }
304 else if (ND_hi(n) <= ND_lo(v)) {
305 agedge(g, n, v, NULL, 1);
306 }
307 }
308 }
309 if (!haveBackedge) return;
310
311 sg = agsubg(g, "comp", 1);
312 Agnode_t **arr = gv_calloc(agnnodes(g), sizeof(Agnode_t*));
313 int *indices = gv_calloc(agnnodes(g), sizeof(int));
314
315 for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
316 if (ND_x(n) || agdegree(g,n,1,1) == 0) continue;
317 if (getComp(g, n, sg, indices)) {
318 int i, sz = agnnodes(sg);
319 cnt = topsort (g, sg, arr);
320 assert (cnt == sz);
321 qsort(indices, cnt, sizeof(int), ordercmpf);
322 for (i = 0; i < sz; i++) {
323 ND_order(arr[i]) = indices[i];
324 rk->v[indices[i]] = arr[i];
325 }
326 }
327 emptyComp(sg);
328 }
329 free(indices);
330 free (arr);
331}
332
333/* Check that the ordering of labels for flat edges is consistent.
334 * This is necessary because dot_position will attempt to force the label
335 * to be between the edge's vertices. This can lead to an infeasible problem.
336 *
337 * We check each rank for any flat edge labels (as dummy nodes) and create a
338 * graph with a node for each label. If the graph contains more than 1 node, we
339 * call fixLabelOrder to see if there really is a problem and, if so, fix it.
340 */
341void
343{
344 int j, r, lo, hi;
345 graph_t* lg = NULL;
346 rank_t* rk;
347 Agnode_t* u;
348 Agnode_t* n;
349 Agedge_t* e;
350
351 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
352 rk = GD_rank(g)+r;
353 for (j = 0; j < rk->n; j++) {
354 u = rk->v[j];
355 if ((e = ND_alg(u))) {
356 if (!lg) lg = agopen ("lg", Agstrictdirected, 0);
357 n = agnode(lg, ITOS(j), 1);
358 agbindrec(n, "info", sizeof(info_t), true);
359 lo = ND_order(aghead(ND_out(u).list[0]));
360 hi = ND_order(aghead(ND_out(u).list[1]));
361 if (lo > hi) {
362 SWAP(&lo, &hi);
363 }
364 ND_lo(n) = lo;
365 ND_hi(n) = hi;
366 ND_np(n) = u;
367 }
368 }
369 if (lg) {
370 if (agnnodes(lg) > 1) fixLabelOrder (lg, rk);
371 agclose(lg);
372 lg = NULL;
373 }
374 }
375}
376
377/* Minimize edge crossings
378 * Note that nodes are not placed into GD_rank(g) until mincross()
379 * is called.
380 */
382 int64_t nc;
383 char *s;
384
385 /* check whether malformed input has led to empty cluster that the crossing
386 * functions will not anticipate
387 */
388 {
389 size_t i;
390 for (i = 1; i <= (size_t)GD_n_cluster(g); ) {
391 if (agfstnode(GD_clust(g)[i]) == NULL) {
392 agwarningf("removing empty cluster\n");
393 memmove(&GD_clust(g)[i], &GD_clust(g)[i + 1],
394 ((size_t)GD_n_cluster(g) - i) * sizeof(GD_clust(g)[0]));
395 --GD_n_cluster(g);
396 } else {
397 ++i;
398 }
399 }
400 }
401
402 init_mincross(g);
403
404 ints_t scratch = {0};
405
406 size_t comp;
407 for (nc = 0, comp = 0; comp < GD_comp(g).size; comp++) {
408 init_mccomp(g, comp);
409 const int64_t mc = mincross(g, 0, &scratch);
410 if (mc < 0) {
411 ints_free(&scratch);
412 return -1;
413 }
414 nc += mc;
415 }
416
417 merge2(g);
418
419 /* run mincross on contents of each cluster */
420 for (int c = 1; c <= GD_n_cluster(g); c++) {
421 const int64_t mc = mincross_clust(GD_clust(g)[c], &scratch);
422 if (mc < 0) {
423 ints_free(&scratch);
424 return -1;
425 }
426 nc += mc;
427#ifdef DEBUG
428 check_vlists(GD_clust(g)[c]);
429 check_order();
430#endif
431 }
432
433 if (GD_n_cluster(g) > 0 && (!(s = agget(g, "remincross")) || mapbool(s))) {
435 ReMincross = true;
436 const int64_t mc = mincross(g, 2, &scratch);
437 if (mc < 0) {
438 ints_free(&scratch);
439 return -1;
440 }
441 nc = mc;
442#ifdef DEBUG
443 for (int c = 1; c <= GD_n_cluster(g); c++)
444 check_vlists(GD_clust(g)[c]);
445#endif
446 }
447 ints_free(&scratch);
448 cleanup2(g, nc);
449 return 0;
450}
451
452static adjmatrix_t *new_matrix(size_t i, size_t j) {
453 adjmatrix_t *rv = gv_alloc(sizeof(adjmatrix_t));
454 rv->nrows = i;
455 rv->ncols = j;
456 return rv;
457}
458
459static void free_matrix(adjmatrix_t * p)
460{
461 if (p) {
462 free(p->data);
463 free(p);
464 }
465}
466
467static void init_mccomp(graph_t *g, size_t c) {
468 int r;
469
470 GD_nlist(g) = GD_comp(g).list[c];
471 if (c > 0) {
472 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
473 GD_rank(g)[r].v = GD_rank(g)[r].v + GD_rank(g)[r].n;
474 GD_rank(g)[r].n = 0;
475 }
476 }
477}
478
479static int betweenclust(edge_t * e)
480{
481 while (ED_to_orig(e))
482 e = ED_to_orig(e);
483 return (ND_clust(agtail(e)) != ND_clust(aghead(e)));
484}
485
486static void do_ordering_node(graph_t *g, node_t *n, bool outflag) {
487 int i, ne;
488 node_t *u, *v;
489 edge_t *e, *f, *fe;
490 edge_t **sortlist = TE_list;
491
492 if (ND_clust(n))
493 return;
494 if (outflag) {
495 for (i = ne = 0; (e = ND_out(n).list[i]); i++)
496 if (!betweenclust(e))
497 sortlist[ne++] = e;
498 } else {
499 for (i = ne = 0; (e = ND_in(n).list[i]); i++)
500 if (!betweenclust(e))
501 sortlist[ne++] = e;
502 }
503 if (ne <= 1)
504 return;
505 /* write null terminator at end of list.
506 requires +1 in TE_list alloccation */
507 sortlist[ne] = 0;
508 qsort(sortlist, ne, sizeof(sortlist[0]), edgeidcmpf);
509 for (ne = 1; (f = sortlist[ne]); ne++) {
510 e = sortlist[ne - 1];
511 if (outflag) {
512 u = aghead(e);
513 v = aghead(f);
514 } else {
515 u = agtail(e);
516 v = agtail(f);
517 }
518 if (find_flat_edge(u, v))
519 return;
520 fe = new_virtual_edge(u, v, NULL);
522 flat_edge(g, fe);
523 }
524}
525
526static void do_ordering(graph_t *g, bool outflag) {
527 /* Order all nodes in graph */
528 node_t *n;
529
530 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
531 do_ordering_node (g, n, outflag);
532 }
533}
534
536{
537 /* Order nodes which have the "ordered" attribute */
538 node_t *n;
539 const char *ordering;
540
541 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
542 if ((ordering = late_string(n, N_ordering, NULL))) {
543 if (streq(ordering, "out"))
544 do_ordering_node(g, n, true);
545 else if (streq(ordering, "in"))
546 do_ordering_node(g, n, false);
547 else if (ordering[0])
548 agerrorf("ordering '%s' not recognized for node '%s'.\n", ordering, agnameof(n));
549 }
550 }
551}
552
553/* handle case where graph specifies edge ordering
554 * If the graph does not have an ordering attribute, we then
555 * check for nodes having the attribute.
556 * Note that, in this implementation, the value of G_ordering
557 * dominates the value of N_ordering.
558 */
559static void ordered_edges(graph_t * g)
560{
561 char *ordering;
562
563 if (!G_ordering && !N_ordering)
564 return;
565 if ((ordering = late_string(g, G_ordering, NULL))) {
566 if (streq(ordering, "out"))
567 do_ordering(g, true);
568 else if (streq(ordering, "in"))
569 do_ordering(g, false);
570 else if (ordering[0])
571 agerrorf("ordering '%s' not recognized.\n", ordering);
572 }
573 else
574 {
575 graph_t *subg;
576
577 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
578 /* clusters are processed by separate calls to ordered_edges */
579 if (!is_cluster(subg))
580 ordered_edges(subg);
581 }
583 }
584}
585
586static int64_t mincross_clust(graph_t *g, ints_t *scratch) {
587 int c;
588
589 if (expand_cluster(g) != 0) {
590 return -1;
591 }
592 ordered_edges(g);
594 flat_reorder(g);
595 int64_t nc = mincross(g, 2, scratch);
596 if (nc < 0) {
597 return nc;
598 }
599
600 for (c = 1; c <= GD_n_cluster(g); c++) {
601 const int64_t mc = mincross_clust(GD_clust(g)[c], scratch);
602 if (mc < 0) {
603 return mc;
604 }
605 nc += mc;
606 }
607
608 save_vlist(g);
609 return nc;
610}
611
612static bool left2right(graph_t *g, node_t *v, node_t *w) {
613 /* CLUSTER indicates orig nodes of clusters, and vnodes of skeletons */
614 if (!ReMincross) {
615 if (ND_clust(v) != ND_clust(w) && ND_clust(v) && ND_clust(w)) {
616 /* the following allows cluster skeletons to be swapped */
617 if (ND_ranktype(v) == CLUSTER && ND_node_type(v) == VIRTUAL)
618 return false;
619 if (ND_ranktype(w) == CLUSTER && ND_node_type(w) == VIRTUAL)
620 return false;
621 return true;
622 }
623 } else {
624 if (ND_clust(v) != ND_clust(w))
625 return true;
626 }
627 adjmatrix_t *const M = GD_rank(g)[ND_rank(v)].flat;
628 if (M == NULL)
629 return false;
630 if (GD_flip(g)) {
631 SWAP(&v, &w);
632 }
633 return matrix_get(M, (size_t)flatindex(v), (size_t)flatindex(w));
634}
635
636static int64_t in_cross(node_t *v, node_t *w) {
637 edge_t **e1, **e2;
638 int inv, t;
639 int64_t cross = 0;
640
641 for (e2 = ND_in(w).list; *e2; e2++) {
642 int cnt = ED_xpenalty(*e2);
643
644 inv = ND_order(agtail(*e2));
645
646 for (e1 = ND_in(v).list; *e1; e1++) {
647 t = ND_order(agtail(*e1)) - inv;
648 if (t > 0 || (t == 0 && ED_tail_port(*e1).p.x > ED_tail_port(*e2).p.x))
649 cross += ED_xpenalty(*e1) * cnt;
650 }
651 }
652 return cross;
653}
654
655static int out_cross(node_t * v, node_t * w)
656{
657 edge_t **e1, **e2;
658 int inv, cross = 0, t;
659
660 for (e2 = ND_out(w).list; *e2; e2++) {
661 int cnt = ED_xpenalty(*e2);
662 inv = ND_order(aghead(*e2));
663
664 for (e1 = ND_out(v).list; *e1; e1++) {
665 t = ND_order(aghead(*e1)) - inv;
666 if (t > 0 || (t == 0 && (ED_head_port(*e1)).p.x > (ED_head_port(*e2)).p.x))
667 cross += ED_xpenalty(*e1) * cnt;
668 }
669 }
670 return cross;
671
672}
673
674static void exchange(node_t * v, node_t * w)
675{
676 int vi, wi, r;
677
678 r = ND_rank(v);
679 vi = ND_order(v);
680 wi = ND_order(w);
681 ND_order(v) = wi;
682 GD_rank(Root)[r].v[wi] = v;
683 ND_order(w) = vi;
684 GD_rank(Root)[r].v[vi] = w;
685}
686
687static int64_t transpose_step(graph_t *g, int r, bool reverse) {
688 int i;
689 node_t *v, *w;
690
691 int64_t rv = 0;
692 GD_rank(g)[r].candidate = false;
693 for (i = 0; i < GD_rank(g)[r].n - 1; i++) {
694 v = GD_rank(g)[r].v[i];
695 w = GD_rank(g)[r].v[i + 1];
696 assert(ND_order(v) < ND_order(w));
697 if (left2right(g, v, w))
698 continue;
699 int64_t c0 = 0;
700 int64_t c1 = 0;
701 if (r > 0) {
702 c0 += in_cross(v, w);
703 c1 += in_cross(w, v);
704 }
705 if (GD_rank(g)[r + 1].n > 0) {
706 c0 += out_cross(v, w);
707 c1 += out_cross(w, v);
708 }
709 if (c1 < c0 || (c0 > 0 && reverse && c1 == c0)) {
710 exchange(v, w);
711 rv += c0 - c1;
712 GD_rank(Root)[r].valid = false;
713 GD_rank(g)[r].candidate = true;
714
715 if (r > GD_minrank(g)) {
716 GD_rank(Root)[r - 1].valid = false;
717 GD_rank(g)[r - 1].candidate = true;
718 }
719 if (r < GD_maxrank(g)) {
720 GD_rank(Root)[r + 1].valid = false;
721 GD_rank(g)[r + 1].candidate = true;
722 }
723 }
724 }
725 return rv;
726}
727
728static void transpose(graph_t * g, bool reverse)
729{
730 int r;
731
732 for (r = GD_minrank(g); r <= GD_maxrank(g); r++)
733 GD_rank(g)[r].candidate = true;
734 int64_t delta;
735 do {
736 delta = 0;
737 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
738 if (GD_rank(g)[r].candidate) {
739 delta += transpose_step(g, r, reverse);
740 }
741 }
742 } while (delta >= 1);
743}
744
745static int64_t mincross(graph_t *g, int startpass, ints_t *scratch) {
746 const int endpass = 2;
747 int maxthispass = 0, iter, trying, pass;
748 int64_t cur_cross, best_cross;
749
750 if (startpass > 1) {
751 cur_cross = best_cross = ncross(scratch);
752 save_best(g);
753 } else
754 cur_cross = best_cross = INT64_MAX;
755 for (pass = startpass; pass <= endpass; pass++) {
756 if (pass <= 1) {
757 maxthispass = MIN(4, MaxIter);
758 if (g == dot_root(g))
759 if (build_ranks(g, pass, scratch) != 0) {
760 return -1;
761 }
762 if (pass == 0)
764 flat_reorder(g);
765
766 if ((cur_cross = ncross(scratch)) <= best_cross) {
767 save_best(g);
768 best_cross = cur_cross;
769 }
770 } else {
771 maxthispass = MaxIter;
772 if (cur_cross > best_cross)
773 restore_best(g);
774 cur_cross = best_cross;
775 }
776 trying = 0;
777 for (iter = 0; iter < maxthispass; iter++) {
778 if (Verbose)
779 fprintf(stderr,
780 "mincross: pass %d iter %d trying %d cur_cross %" PRId64 " best_cross %"
781 PRId64 "\n",
782 pass, iter, trying, cur_cross, best_cross);
783 if (trying++ >= MinQuit)
784 break;
785 if (cur_cross == 0)
786 break;
787 mincross_step(g, iter);
788 if ((cur_cross = ncross(scratch)) <= best_cross) {
789 save_best(g);
790 if (cur_cross < Convergence * (double)best_cross)
791 trying = 0;
792 best_cross = cur_cross;
793 }
794 }
795 if (cur_cross == 0)
796 break;
797 }
798 if (cur_cross > best_cross)
799 restore_best(g);
800 if (best_cross > 0) {
801 transpose(g, false);
802 best_cross = ncross(scratch);
803 }
804
805 return best_cross;
806}
807
808static void restore_best(graph_t * g)
809{
810 node_t *n;
811 int i, r;
812
813 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
814 for (i = 0; i < GD_rank(g)[r].n; i++) {
815 n = GD_rank(g)[r].v[i];
816 ND_order(n) = saveorder(n);
817 }
818 }
819 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
820 GD_rank(Root)[r].valid = false;
821 qsort(GD_rank(g)[r].v, GD_rank(g)[r].n, sizeof(GD_rank(g)[0].v[0]),
823 }
824}
825
826static void save_best(graph_t * g)
827{
828 node_t *n;
829 int i, r;
830 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
831 for (i = 0; i < GD_rank(g)[r].n; i++) {
832 n = GD_rank(g)[r].v[i];
833 saveorder(n) = ND_order(n);
834 }
835 }
836}
837
838/* merges the connected components of g */
839static void merge_components(graph_t * g)
840{
841 node_t *u, *v;
842
843 if (GD_comp(g).size <= 1)
844 return;
845 u = NULL;
846 for (size_t c = 0; c < GD_comp(g).size; c++) {
847 v = GD_comp(g).list[c];
848 if (u)
849 ND_next(u) = v;
850 ND_prev(v) = u;
851 while (ND_next(v)) {
852 v = ND_next(v);
853 }
854 u = v;
855 }
856 GD_comp(g).size = 1;
857 GD_nlist(g) = GD_comp(g).list[0];
860}
861
862/* merge connected components, create globally consistent rank lists */
863static void merge2(graph_t * g)
864{
865 int i, r;
866 node_t *v;
867
868 /* merge the components and rank limits */
870
871 /* install complete ranks */
872 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
873 GD_rank(g)[r].n = GD_rank(g)[r].an;
874 GD_rank(g)[r].v = GD_rank(g)[r].av;
875 for (i = 0; i < GD_rank(g)[r].n; i++) {
876 v = GD_rank(g)[r].v[i];
877 if (v == NULL) {
878 if (Verbose)
879 fprintf(stderr,
880 "merge2: graph %s, rank %d has only %d < %d nodes\n",
881 agnameof(g), r, i, GD_rank(g)[r].n);
882 GD_rank(g)[r].n = i;
883 break;
884 }
885 ND_order(v) = i;
886 }
887 }
888}
889
890static void cleanup2(graph_t *g, int64_t nc) {
891 int i, j, r, c;
892 node_t *v;
893 edge_t *e;
894
895 if (TI_list) {
896 free(TI_list);
897 TI_list = NULL;
898 }
899 if (TE_list) {
900 free(TE_list);
901 TE_list = NULL;
902 }
903 /* fix vlists of clusters */
904 for (c = 1; c <= GD_n_cluster(g); c++)
906
907 /* remove node temporary edges for ordering nodes */
908 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
909 for (i = 0; i < GD_rank(g)[r].n; i++) {
910 v = GD_rank(g)[r].v[i];
911 ND_order(v) = i;
912 if (ND_flat_out(v).list) {
913 for (j = 0; (e = ND_flat_out(v).list[j]); j++)
914 if (ED_edge_type(e) == FLATORDER) {
916 free(e->base.data);
917 free(e);
918 j--;
919 }
920 }
921 }
922 free_matrix(GD_rank(g)[r].flat);
923 }
924 if (Verbose)
925 fprintf(stderr, "mincross %s: %" PRId64 " crossings, %.2f secs.\n",
926 agnameof(g), nc, elapsed_sec());
927}
928
929static node_t *neighbor(node_t * v, int dir)
930{
931 node_t *rv = NULL;
932assert(v);
933 if (dir < 0) {
934 if (ND_order(v) > 0)
935 rv = GD_rank(Root)[ND_rank(v)].v[ND_order(v) - 1];
936 } else
937 rv = GD_rank(Root)[ND_rank(v)].v[ND_order(v) + 1];
938assert(rv == 0 || (ND_order(rv)-ND_order(v))*dir > 0);
939 return rv;
940}
941
942static bool is_a_normal_node_of(graph_t *g, node_t *v) {
943 return ND_node_type(v) == NORMAL && agcontains(g, v);
944}
945
947 if (ND_node_type(v) == VIRTUAL
948 && ND_in(v).size == 1 && ND_out(v).size == 1) {
949 edge_t *e = ND_out(v).list[0];
950 while (ED_edge_type(e) != NORMAL)
951 e = ED_to_orig(e);
952 if (agcontains(g, e))
953 return true;
954 }
955 return false;
956}
957
958static bool inside_cluster(graph_t *g, node_t *v) {
959 return is_a_normal_node_of(g, v) || is_a_vnode_of_an_edge_of(g, v);
960}
961
962static node_t *furthestnode(graph_t * g, node_t * v, int dir)
963{
964 node_t *rv = v;
965 for (node_t *u = v; (u = neighbor(u, dir)); ) {
966 if (is_a_normal_node_of(g, u))
967 rv = u;
968 else if (is_a_vnode_of_an_edge_of(g, u))
969 rv = u;
970 }
971 return rv;
972}
973
975{
976 int r;
977
978 if (GD_rankleader(g))
979 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
980 GD_rankleader(g)[r] = GD_rank(g)[r].v[0];
981 }
982}
983
985{
986 int c;
987
988 save_vlist(g);
989 for (c = 1; c <= GD_n_cluster(g); c++)
991}
992
993
995{
996 // fix vlists of sub-clusters
997 for (int c = 1; c <= GD_n_cluster(g); c++)
999
1000 if (GD_rankleader(g))
1001 for (int r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1002 node_t *const v = GD_rankleader(g)[r];
1003 if (v == NULL) {
1004 continue;
1005 }
1006#ifdef DEBUG
1007 node_in_root_vlist(v);
1008#endif
1009 node_t *const u = furthestnode(g, v, -1);
1010 node_t *const w = furthestnode(g, v, 1);
1011 GD_rankleader(g)[r] = u;
1012#ifdef DEBUG
1013 assert(GD_rank(dot_root(g))[r].v[ND_order(u)] == u);
1014#endif
1015 GD_rank(g)[r].v = GD_rank(dot_root(g))[r].v + ND_order(u);
1016 GD_rank(g)[r].n = ND_order(w) - ND_order(u) + 1;
1017 }
1018}
1019
1020/* The structures in crossing minimization and positioning require
1021 * that clusters have some node on each rank. This function recursively
1022 * guarantees this property. It takes into account nodes and edges in
1023 * a cluster, the latter causing dummy nodes for intervening ranks.
1024 * For any rank without node, we create a real node of small size. This
1025 * is stored in the subgraph sg, for easy removal later.
1026 *
1027 * I believe it is not necessary to do this for the root graph, as these
1028 * are laid out one component at a time and these will necessarily have a
1029 * node on each rank from source to sink levels.
1030 */
1031static Agraph_t*
1032realFillRanks (Agraph_t* g, int rnks[], int rnks_sz, Agraph_t* sg)
1033{
1034 int i, c;
1035 Agedge_t* e;
1036 Agnode_t* n;
1037
1038 for (c = 1; c <= GD_n_cluster(g); c++)
1039 sg = realFillRanks (GD_clust(g)[c], rnks, rnks_sz, sg);
1040
1041 if (dot_root(g) == g)
1042 return sg;
1043 memset (rnks, 0, sizeof(int)*rnks_sz);
1044 for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
1045 rnks[ND_rank(n)] = 1;
1046 for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
1047 for (i = ND_rank(n)+1; i <= ND_rank(aghead(e)); i++)
1048 rnks[i] = 1;
1049 }
1050 }
1051 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
1052 if (rnks[i] == 0) {
1053 if (!sg) {
1054 sg = agsubg (dot_root(g), "_new_rank", 1);
1055 }
1056 n = agnode (sg, NULL, 1);
1057 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
1058 ND_rank(n) = i;
1059 ND_lw(n) = ND_rw(n) = 0.5;
1060 ND_ht(n) = 1;
1061 ND_UF_size(n) = 1;
1062 alloc_elist(4, ND_in(n));
1063 alloc_elist(4, ND_out(n));
1064 agsubnode (g, n, 1);
1065 }
1066 }
1067 return sg;
1068}
1069
1070static void
1072{
1073 int rnks_sz = GD_maxrank(g) + 2;
1074 int *rnks = gv_calloc(rnks_sz, sizeof(int));
1075 realFillRanks (g, rnks, rnks_sz, NULL);
1076 free (rnks);
1077}
1078
1079static void init_mincross(graph_t * g)
1080{
1081 int size;
1082
1083 if (Verbose)
1084 start_timer();
1085
1086 ReMincross = false;
1087 Root = g;
1088 /* alloc +1 for the null terminator usage in do_ordering() */
1089 size = agnedges(dot_root(g)) + 1;
1090 TE_list = gv_calloc(size, sizeof(edge_t*));
1091 TI_list = gv_calloc(size, sizeof(int));
1093 if (GD_flags(g) & NEW_RANK)
1094 fillRanks (g);
1095 class2(g);
1096 decompose(g, 1);
1097 allocate_ranks(g);
1098 ordered_edges(g);
1101}
1102
1103static void flat_rev(Agraph_t * g, Agedge_t * e)
1104{
1105 int j;
1106 Agedge_t *rev;
1107
1108 if (!ND_flat_out(aghead(e)).list)
1109 rev = NULL;
1110 else
1111 for (j = 0; (rev = ND_flat_out(aghead(e)).list[j]); j++)
1112 if (aghead(rev) == agtail(e))
1113 break;
1114 if (rev) {
1115 merge_oneway(e, rev);
1116 if (ED_edge_type(rev) == FLATORDER && ED_to_orig(rev) == 0)
1117 ED_to_orig(rev) = e;
1119 } else {
1120 rev = new_virtual_edge(aghead(e), agtail(e), e);
1121 if (ED_edge_type(e) == FLATORDER)
1122 ED_edge_type(rev) = FLATORDER;
1123 else
1124 ED_edge_type(rev) = REVERSED;
1125 ED_label(rev) = ED_label(e);
1126 flat_edge(g, rev);
1127 }
1128}
1129
1130static void flat_search(graph_t * g, node_t * v)
1131{
1132 int i;
1133 bool hascl;
1134 edge_t *e;
1135 adjmatrix_t *M = GD_rank(g)[ND_rank(v)].flat;
1136
1137 ND_mark(v) = true;
1138 ND_onstack(v) = true;
1139 hascl = GD_n_cluster(dot_root(g)) > 0;
1140 if (ND_flat_out(v).list)
1141 for (i = 0; (e = ND_flat_out(v).list[i]); i++) {
1142 if (hascl && !(agcontains(g, agtail(e)) && agcontains(g, aghead(e))))
1143 continue;
1144 if (ED_weight(e) == 0)
1145 continue;
1146 if (ND_onstack(aghead(e))) {
1147 assert(flatindex(aghead(e)) < M->nrows);
1148 assert(flatindex(agtail(e)) < M->ncols);
1149 matrix_set(M, (size_t)flatindex(aghead(e)), (size_t)flatindex(agtail(e)));
1151 i--;
1152 if (ED_edge_type(e) == FLATORDER)
1153 continue;
1154 flat_rev(g, e);
1155 } else {
1156 assert(flatindex(aghead(e)) < M->nrows);
1157 assert(flatindex(agtail(e)) < M->ncols);
1158 matrix_set(M, (size_t)flatindex(agtail(e)), (size_t)flatindex(aghead(e)));
1159 if (!ND_mark(aghead(e)))
1160 flat_search(g, aghead(e));
1161 }
1162 }
1163 ND_onstack(v) = false;
1164}
1165
1167{
1168 int i, r;
1169 node_t *v;
1170
1171 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1172 bool flat = false;
1173 for (i = 0; i < GD_rank(g)[r].n; i++) {
1174 v = GD_rank(g)[r].v[i];
1175 ND_mark(v) = false;
1176 ND_onstack(v) = false;
1177 ND_low(v) = i;
1178 if (ND_flat_out(v).size > 0 && !flat) {
1179 GD_rank(g)[r].flat =
1180 new_matrix((size_t)GD_rank(g)[r].n, (size_t)GD_rank(g)[r].n);
1181 flat = true;
1182 }
1183 }
1184 if (flat) {
1185 for (i = 0; i < GD_rank(g)[r].n; i++) {
1186 v = GD_rank(g)[r].v[i];
1187 if (!ND_mark(v))
1188 flat_search(g, v);
1189 }
1190 }
1191 }
1192}
1193
1194/* Allocate rank structure, determining number of nodes per rank.
1195 * Note that no nodes are put into the structure yet.
1196 */
1198{
1199 int r, low, high;
1200 node_t *n;
1201 edge_t *e;
1202
1203 int *cn = gv_calloc(GD_maxrank(g) + 2, sizeof(int)); // must be 0 based, not GD_minrank
1204 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1205 cn[ND_rank(n)]++;
1206 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
1207 low = ND_rank(agtail(e));
1208 high = ND_rank(aghead(e));
1209 if (low > high) {
1210 SWAP(&low, &high);
1211 }
1212 for (r = low + 1; r < high; r++)
1213 cn[r]++;
1214 }
1215 }
1216 GD_rank(g) = gv_calloc(GD_maxrank(g) + 2, sizeof(rank_t));
1217 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1218 GD_rank(g)[r].an = GD_rank(g)[r].n = cn[r] + 1;
1219 GD_rank(g)[r].av = GD_rank(g)[r].v = gv_calloc(cn[r] + 1, sizeof(node_t*));
1220 }
1221 free(cn);
1222}
1223
1224/* install a node at the current right end of its rank */
1226 int i, r;
1227
1228 r = ND_rank(n);
1229 i = GD_rank(g)[r].n;
1230 if (GD_rank(g)[r].an <= 0) {
1231 agerrorf("install_in_rank, line %d: %s %s rank %d i = %d an = 0\n",
1232 __LINE__, agnameof(g), agnameof(n), r, i);
1233 return -1;
1234 }
1235
1236 GD_rank(g)[r].v[i] = n;
1237 ND_order(n) = i;
1238 GD_rank(g)[r].n++;
1239 assert(GD_rank(g)[r].n <= GD_rank(g)[r].an);
1240#ifdef DEBUG
1241 {
1242 node_t *v;
1243
1244 for (v = GD_nlist(g); v; v = ND_next(v))
1245 if (v == n)
1246 break;
1247 assert(v != NULL);
1248 }
1249#endif
1250 if (ND_order(n) > GD_rank(Root)[r].an) {
1251 agerrorf("install_in_rank, line %d: ND_order(%s) [%d] > GD_rank(Root)[%d].an [%d]\n",
1252 __LINE__, agnameof(n), ND_order(n), r, GD_rank(Root)[r].an);
1253 return -1;
1254 }
1255 if (r < GD_minrank(g) || r > GD_maxrank(g)) {
1256 agerrorf("install_in_rank, line %d: rank %d not in rank range [%d,%d]\n",
1257 __LINE__, r, GD_minrank(g), GD_maxrank(g));
1258 return -1;
1259 }
1260 if (GD_rank(g)[r].v + ND_order(n) >
1261 GD_rank(g)[r].av + GD_rank(Root)[r].an) {
1262 agerrorf("install_in_rank, line %d: GD_rank(g)[%d].v + ND_order(%s) [%d] > GD_rank(g)[%d].av + GD_rank(Root)[%d].an [%d]\n",
1263 __LINE__, r, agnameof(n),ND_order(n), r, r, GD_rank(Root)[r].an);
1264 return -1;
1265 }
1266 return 0;
1267}
1268
1269/* install nodes in ranks. the initial ordering ensure that series-parallel
1270 * graphs such as trees are drawn with no crossings. it tries searching
1271 * in- and out-edges and takes the better of the two initial orderings.
1272 */
1273int build_ranks(graph_t *g, int pass, ints_t *scratch) {
1274 int i, j;
1275 node_t *n, *ns;
1276 edge_t **otheredges;
1277 node_queue_t q = {0};
1278 for (n = GD_nlist(g); n; n = ND_next(n))
1279 MARK(n) = false;
1280
1281#ifdef DEBUG
1282 {
1283 edge_t *e;
1284 for (n = GD_nlist(g); n; n = ND_next(n)) {
1285 for (i = 0; (e = ND_out(n).list[i]); i++)
1286 assert(!MARK(aghead(e)));
1287 for (i = 0; (e = ND_in(n).list[i]); i++)
1288 assert(!MARK(agtail(e)));
1289 }
1290 }
1291#endif
1292
1293 for (i = GD_minrank(g); i <= GD_maxrank(g); i++)
1294 GD_rank(g)[i].n = 0;
1295
1296 const bool walkbackwards = g != agroot(g); // if this is a cluster, need to
1297 // walk GD_nlist backward to
1298 // preserve input node order
1299 if (walkbackwards) {
1300 for (ns = GD_nlist(g); ND_next(ns); ns = ND_next(ns)) {
1301 ;
1302 }
1303 } else {
1304 ns = GD_nlist(g);
1305 }
1306 for (n = ns; n; n = walkbackwards ? ND_prev(n) : ND_next(n)) {
1307 otheredges = pass == 0 ? ND_in(n).list : ND_out(n).list;
1308 if (otheredges[0] != NULL)
1309 continue;
1310 if (!MARK(n)) {
1311 MARK(n) = true;
1312 node_queue_push_back(&q, n);
1313 while (!node_queue_is_empty(&q)) {
1314 node_t *n0 = node_queue_pop_front(&q);
1315 if (ND_ranktype(n0) != CLUSTER) {
1316 if (install_in_rank(g, n0) != 0) {
1317 node_queue_free(&q);
1318 return -1;
1319 }
1320 enqueue_neighbors(&q, n0, pass);
1321 } else {
1322 const int rc = install_cluster(g, n0, pass, &q);
1323 if (rc != 0) {
1324 node_queue_free(&q);
1325 return rc;
1326 }
1327 }
1328 }
1329 }
1330 }
1331 assert(node_queue_is_empty(&q));
1332 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
1333 GD_rank(Root)[i].valid = false;
1334 if (GD_flip(g) && GD_rank(g)[i].n > 0) {
1335 node_t **vlist = GD_rank(g)[i].v;
1336 int num_nodes_1 = GD_rank(g)[i].n - 1;
1337 int half_num_nodes_1 = num_nodes_1 / 2;
1338 for (j = 0; j <= half_num_nodes_1; j++)
1339 exchange(vlist[j], vlist[num_nodes_1 - j]);
1340 }
1341 }
1342
1343 if (g == dot_root(g) && ncross(scratch) > 0)
1344 transpose(g, false);
1345 node_queue_free(&q);
1346 return 0;
1347}
1348
1349void enqueue_neighbors(node_queue_t *q, node_t *n0, int pass) {
1350 edge_t *e;
1351
1352 if (pass == 0) {
1353 for (size_t i = 0; i < ND_out(n0).size; i++) {
1354 e = ND_out(n0).list[i];
1355 if (!MARK(aghead(e))) {
1356 MARK(aghead(e)) = true;
1357 node_queue_push_back(q, aghead(e));
1358 }
1359 }
1360 } else {
1361 for (size_t i = 0; i < ND_in(n0).size; i++) {
1362 e = ND_in(n0).list[i];
1363 if (!MARK(agtail(e))) {
1364 MARK(agtail(e)) = true;
1365 node_queue_push_back(q, agtail(e));
1366 }
1367 }
1368 }
1369}
1370
1372 if (ED_weight(e) == 0)
1373 return false;
1374 if (!inside_cluster(g, agtail(e)))
1375 return false;
1376 if (!inside_cluster(g, aghead(e)))
1377 return false;
1378 return true;
1379}
1380
1381DEFINE_LIST(nodes, node_t *)
1382
1383/* construct nodes reachable from 'here' in post-order.
1384* This is the same as doing a topological sort in reverse order.
1385*/
1386static void postorder(graph_t *g, node_t *v, nodes_t *list, int r) {
1387 edge_t *e;
1388 int i;
1389
1390 MARK(v) = true;
1391 if (ND_flat_out(v).size > 0) {
1392 for (i = 0; (e = ND_flat_out(v).list[i]); i++) {
1393 if (!constraining_flat_edge(g, e)) continue;
1394 if (!MARK(aghead(e)))
1395 postorder(g, aghead(e), list, r);
1396 }
1397 }
1398 assert(ND_rank(v) == r);
1399 nodes_append(list, v);
1400}
1401
1402static void flat_reorder(graph_t * g)
1403{
1404 int i, r, local_in_cnt, local_out_cnt, base_order;
1405 node_t *v;
1406 nodes_t temprank = {0};
1407 edge_t *flat_e, *e;
1408
1409 if (!GD_has_flat_edges(g))
1410 return;
1411 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1412 if (GD_rank(g)[r].n == 0) continue;
1413 base_order = ND_order(GD_rank(g)[r].v[0]);
1414 for (i = 0; i < GD_rank(g)[r].n; i++)
1415 MARK(GD_rank(g)[r].v[i]) = false;
1416 nodes_clear(&temprank);
1417
1418 /* construct reverse topological sort order in temprank */
1419 for (i = 0; i < GD_rank(g)[r].n; i++) {
1420 if (GD_flip(g)) v = GD_rank(g)[r].v[i];
1421 else v = GD_rank(g)[r].v[GD_rank(g)[r].n - i - 1];
1422
1423 local_in_cnt = local_out_cnt = 0;
1424 for (size_t j = 0; j < ND_flat_in(v).size; j++) {
1425 flat_e = ND_flat_in(v).list[j];
1426 if (constraining_flat_edge(g, flat_e)) local_in_cnt++;
1427 }
1428 for (size_t j = 0; j < ND_flat_out(v).size; j++) {
1429 flat_e = ND_flat_out(v).list[j];
1430 if (constraining_flat_edge(g, flat_e)) local_out_cnt++;
1431 }
1432 if (local_in_cnt == 0 && local_out_cnt == 0)
1433 nodes_append(&temprank, v);
1434 else {
1435 if (!MARK(v) && local_in_cnt == 0) {
1436 postorder(g, v, &temprank, r);
1437 }
1438 }
1439 }
1440
1441 if (nodes_size(&temprank) > 0) {
1442 if (!GD_flip(g)) {
1443 nodes_reverse(&temprank);
1444 }
1445 for (i = 0; i < GD_rank(g)[r].n; i++) {
1446 v = GD_rank(g)[r].v[i] = nodes_get(&temprank, (size_t)i);
1447 ND_order(v) = i + base_order;
1448 }
1449
1450 /* nonconstraint flat edges must be made LR */
1451 for (i = 0; i < GD_rank(g)[r].n; i++) {
1452 v = GD_rank(g)[r].v[i];
1453 if (ND_flat_out(v).list) {
1454 for (size_t j = 0; (e = ND_flat_out(v).list[j]); j++) {
1455 if ((!GD_flip(g) && ND_order(aghead(e)) < ND_order(agtail(e))) ||
1456 (GD_flip(g) && ND_order(aghead(e)) > ND_order(agtail(e)))) {
1457 assert(!constraining_flat_edge(g, e));
1459 j--;
1460 flat_rev(g, e);
1461 }
1462 }
1463 }
1464 }
1465 /* postprocess to restore intended order */
1466 }
1467 /* else do no harm! */
1468 GD_rank(Root)[r].valid = false;
1469 }
1470 nodes_free(&temprank);
1471}
1472
1473static void reorder(graph_t * g, int r, bool reverse, bool hasfixed)
1474{
1475 int changed = 0, nelt;
1476 node_t **vlist = GD_rank(g)[r].v;
1477 node_t **lp, **rp, **ep = vlist + GD_rank(g)[r].n;
1478
1479 for (nelt = GD_rank(g)[r].n - 1; nelt >= 0; nelt--) {
1480 lp = vlist;
1481 while (lp < ep) {
1482 /* find leftmost node that can be compared */
1483 while (lp < ep && ND_mval(*lp) < 0)
1484 lp++;
1485 if (lp >= ep)
1486 break;
1487 /* find the node that can be compared */
1488 bool sawclust = false;
1489 bool muststay = false;
1490 for (rp = lp + 1; rp < ep; rp++) {
1491 if (sawclust && ND_clust(*rp))
1492 continue; /* ### */
1493 if (left2right(g, *lp, *rp)) {
1494 muststay = true;
1495 break;
1496 }
1497 if (ND_mval(*rp) >= 0)
1498 break;
1499 if (ND_clust(*rp))
1500 sawclust = true; /* ### */
1501 }
1502 if (rp >= ep)
1503 break;
1504 if (!muststay) {
1505 const double p1 = ND_mval(*lp);
1506 const double p2 = ND_mval(*rp);
1507 if (p1 > p2 || (p1 >= p2 && reverse)) {
1508 exchange(*lp, *rp);
1509 changed++;
1510 }
1511 }
1512 lp = rp;
1513 }
1514 if (!hasfixed && !reverse)
1515 ep--;
1516 }
1517
1518 if (changed) {
1519 GD_rank(Root)[r].valid = false;
1520 if (r > 0)
1521 GD_rank(Root)[r - 1].valid = false;
1522 }
1523}
1524
1525static void mincross_step(graph_t * g, int pass)
1526{
1527 int r, other, first, last, dir;
1528
1529 bool reverse = pass % 4 < 2;
1530
1531 if (pass % 2 == 0) { /* down pass */
1532 first = GD_minrank(g) + 1;
1533 if (GD_minrank(g) > GD_minrank(Root))
1534 first--;
1535 last = GD_maxrank(g);
1536 dir = 1;
1537 } else { /* up pass */
1538 first = GD_maxrank(g) - 1;
1539 last = GD_minrank(g);
1540 if (GD_maxrank(g) < GD_maxrank(Root))
1541 first++;
1542 dir = -1;
1543 }
1544
1545 for (r = first; r != last + dir; r += dir) {
1546 other = r - dir;
1547 bool hasfixed = medians(g, r, other);
1548 reorder(g, r, reverse, hasfixed);
1549 }
1550 transpose(g, !reverse);
1551}
1552
1553static int local_cross(elist l, int dir)
1554{
1555 int i, j;
1556 int cross = 0;
1557 edge_t *e, *f;
1558 bool is_out = dir > 0;
1559 for (i = 0; (e = l.list[i]); i++) {
1560 if (is_out)
1561 for (j = i + 1; (f = l.list[j]); j++) {
1562 if ((ND_order(aghead(f)) - ND_order(aghead(e)))
1563 * (ED_tail_port(f).p.x - ED_tail_port(e).p.x) < 0)
1564 cross += ED_xpenalty(e) * ED_xpenalty(f);
1565 } else
1566 for (j = i + 1; (f = l.list[j]); j++) {
1567 if ((ND_order(agtail(f)) - ND_order(agtail(e)))
1568 * (ED_head_port(f).p.x - ED_head_port(e).p.x) < 0)
1569 cross += ED_xpenalty(e) * ED_xpenalty(f);
1570 }
1571 }
1572 return cross;
1573}
1574
1575static int64_t rcross(graph_t *g, int r, ints_t *Count) {
1576 int top, bot, max, i, k;
1577 node_t **rtop, *v;
1578
1579 int64_t cross = 0;
1580 max = 0;
1581 rtop = GD_rank(g)[r].v;
1582
1583 // discard any data from previous runs
1584 ints_clear(Count);
1585
1586 for (top = 0; top < GD_rank(g)[r].n; top++) {
1587 edge_t *e;
1588 if (max > 0) {
1589 for (i = 0; (e = ND_out(rtop[top]).list[i]); i++) {
1590 for (k = ND_order(aghead(e)) + 1; k <= max; k++)
1591 cross += ints_size(Count) <= (size_t)k
1592 ? 0
1593 : ints_get(Count, (size_t)k) * ED_xpenalty(e);
1594 }
1595 }
1596 for (i = 0; (e = ND_out(rtop[top]).list[i]); i++) {
1597 int inv = ND_order(aghead(e));
1598 if (inv > max)
1599 max = inv;
1600 const size_t inv_z = (size_t)inv;
1601 if (ints_size(Count) <= inv_z) {
1602 ints_resize(Count, inv_z + 1, 0);
1603 }
1604 ints_set(Count, inv_z, ints_get(Count, inv_z) + ED_xpenalty(e));
1605 }
1606 }
1607 for (top = 0; top < GD_rank(g)[r].n; top++) {
1608 v = GD_rank(g)[r].v[top];
1609 if (ND_has_port(v))
1610 cross += local_cross(ND_out(v), 1);
1611 }
1612 for (bot = 0; bot < GD_rank(g)[r + 1].n; bot++) {
1613 v = GD_rank(g)[r + 1].v[bot];
1614 if (ND_has_port(v))
1615 cross += local_cross(ND_in(v), -1);
1616 }
1617 return cross;
1618}
1619
1620static int64_t ncross(ints_t *scratch) {
1621 assert(scratch != NULL);
1622 int r;
1623
1624 graph_t *g = Root;
1625 int64_t count = 0;
1626 for (r = GD_minrank(g); r < GD_maxrank(g); r++) {
1627 if (GD_rank(g)[r].valid)
1628 count += GD_rank(g)[r].cache_nc;
1629 else {
1630 const int64_t nc = GD_rank(g)[r].cache_nc = rcross(g, r, scratch);
1631 count += nc;
1632 GD_rank(g)[r].valid = true;
1633 }
1634 }
1635 return count;
1636}
1637
1638static int ordercmpf(const void *x, const void *y) {
1639 const int *i0 = x;
1640 const int *i1 = y;
1641 if (*i0 < *i1) {
1642 return -1;
1643 }
1644 if (*i0 > *i1) {
1645 return 1;
1646 }
1647 return 0;
1648}
1649
1650/* Calculate a mval for nodes with no in or out non-flat edges.
1651 * Assume (ND_out(n).size == 0) && (ND_in(n).size == 0)
1652 * Find flat edge a->n where a has the largest order and set
1653 * n.mval = a.mval+1, assuming a.mval is defined (>=0).
1654 * If there are no flat in edges, find flat edge n->a where a
1655 * has the smallest order and set * n.mval = a.mval-1, assuming
1656 * a.mval is > 0.
1657 * Return true if n.mval is left -1, indicating a fixed node for sorting.
1658 */
1659static bool flat_mval(node_t * n)
1660{
1661 int i;
1662 edge_t *e, **fl;
1663 node_t *nn;
1664
1665 if (ND_flat_in(n).size > 0) {
1666 fl = ND_flat_in(n).list;
1667 nn = agtail(fl[0]);
1668 for (i = 1; (e = fl[i]); i++)
1669 if (ND_order(agtail(e)) > ND_order(nn))
1670 nn = agtail(e);
1671 if (ND_mval(nn) >= 0) {
1672 ND_mval(n) = ND_mval(nn) + 1;
1673 return false;
1674 }
1675 } else if (ND_flat_out(n).size > 0) {
1676 fl = ND_flat_out(n).list;
1677 nn = aghead(fl[0]);
1678 for (i = 1; (e = fl[i]); i++)
1679 if (ND_order(aghead(e)) < ND_order(nn))
1680 nn = aghead(e);
1681 if (ND_mval(nn) > 0) {
1682 ND_mval(n) = ND_mval(nn) - 1;
1683 return false;
1684 }
1685 }
1686 return true;
1687}
1688
1689#define VAL(node,port) (MC_SCALE * ND_order(node) + (port).order)
1690
1691static bool medians(graph_t * g, int r0, int r1)
1692{
1693 int i, j0, lspan, rspan, *list;
1694 node_t *n, **v;
1695 edge_t *e;
1696 bool hasfixed = false;
1697
1698 list = TI_list;
1699 v = GD_rank(g)[r0].v;
1700 for (i = 0; i < GD_rank(g)[r0].n; i++) {
1701 n = v[i];
1702 size_t j = 0;
1703 if (r1 > r0)
1704 for (j0 = 0; (e = ND_out(n).list[j0]); j0++) {
1705 if (ED_xpenalty(e) > 0)
1706 list[j++] = VAL(aghead(e), ED_head_port(e));
1707 } else
1708 for (j0 = 0; (e = ND_in(n).list[j0]); j0++) {
1709 if (ED_xpenalty(e) > 0)
1710 list[j++] = VAL(agtail(e), ED_tail_port(e));
1711 }
1712 switch (j) {
1713 case 0:
1714 ND_mval(n) = -1;
1715 break;
1716 case 1:
1717 ND_mval(n) = list[0];
1718 break;
1719 case 2:
1720 ND_mval(n) = (list[0] + list[1]) / 2;
1721 break;
1722 default:
1723 qsort(list, j, sizeof(int), ordercmpf);
1724 if (j % 2)
1725 ND_mval(n) = list[j / 2];
1726 else {
1727 /* weighted median */
1728 size_t rm = j / 2;
1729 size_t lm = rm - 1;
1730 rspan = list[j - 1] - list[rm];
1731 lspan = list[lm] - list[0];
1732 if (lspan == rspan)
1733 ND_mval(n) = (list[lm] + list[rm]) / 2;
1734 else {
1735 double w = list[lm] * (double)rspan + list[rm] * (double)lspan;
1736 ND_mval(n) = w / (lspan + rspan);
1737 }
1738 }
1739 }
1740 }
1741 for (i = 0; i < GD_rank(g)[r0].n; i++) {
1742 n = v[i];
1743 if ((ND_out(n).size == 0) && (ND_in(n).size == 0))
1744 hasfixed |= flat_mval(n);
1745 }
1746 return hasfixed;
1747}
1748
1749static int nodeposcmpf(const void *x, const void *y) {
1750// Suppress Clang/GCC -Wcast-qual warning. Casting away const here is acceptable
1751// as the later usage is const. We need the cast because the macros use
1752// non-const pointers for genericity.
1753#ifdef __GNUC__
1754#pragma GCC diagnostic push
1755#pragma GCC diagnostic ignored "-Wcast-qual"
1756#endif
1757 node_t **n0 = (node_t **)x;
1758 node_t **n1 = (node_t **)y;
1759#ifdef __GNUC__
1760#pragma GCC diagnostic pop
1761#endif
1762 if (ND_order(*n0) < ND_order(*n1)) {
1763 return -1;
1764 }
1765 if (ND_order(*n0) > ND_order(*n1)) {
1766 return 1;
1767 }
1768 return 0;
1769}
1770
1771static int edgeidcmpf(const void *x, const void *y) {
1772// Suppress Clang/GCC -Wcast-qual warning. Casting away const here is acceptable
1773// as the later usage is const. We need the cast because the macros use
1774// non-const pointers for genericity.
1775#ifdef __GNUC__
1776#pragma GCC diagnostic push
1777#pragma GCC diagnostic ignored "-Wcast-qual"
1778#endif
1779 edge_t **e0 = (edge_t **)x;
1780 edge_t **e1 = (edge_t **)y;
1781#ifdef __GNUC__
1782#pragma GCC diagnostic pop
1783#endif
1784 if (AGSEQ(*e0) < AGSEQ(*e1)) {
1785 return -1;
1786 }
1787 if (AGSEQ(*e0) > AGSEQ(*e1)) {
1788 return 1;
1789 }
1790 return 0;
1791}
1792
1793/* following code deals with weights of edges of "virtual" nodes */
1794#define ORDINARY 0
1795#define SINGLETON 1
1796#define VIRTUALNODE 2
1797#define NTYPES 3
1798
1799#define C_EE 1
1800#define C_VS 2
1801#define C_SS 2
1802#define C_VV 4
1803
1804static int table[NTYPES][NTYPES] = {
1805 /* ordinary */ {C_EE, C_EE, C_EE},
1806 /* singleton */ {C_EE, C_SS, C_VS},
1807 /* virtual */ {C_EE, C_VS, C_VV}
1808};
1809
1810static int endpoint_class(node_t * n)
1811{
1812 if (ND_node_type(n) == VIRTUAL)
1813 return VIRTUALNODE;
1814 if (ND_weight_class(n) <= 1)
1815 return SINGLETON;
1816 return ORDINARY;
1817}
1818
1820{
1821 int t;
1823
1824 /* check whether the upcoming computation will overflow */
1825 assert(t >= 0);
1826 if (INT_MAX / t < ED_weight(e)) {
1827 agerrorf("overflow when calculating virtual weight of edge\n");
1828 graphviz_exit(EXIT_FAILURE);
1829 }
1830
1831 ED_weight(e) *= t;
1832}
1833
1834#ifdef DEBUG
1835void check_rs(graph_t * g, int null_ok)
1836{
1837 int i, r;
1838 node_t *v, *prev;
1839
1840 fprintf(stderr, "\n\n%s:\n", agnameof(g));
1841 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1842 fprintf(stderr, "%d: ", r);
1843 prev = NULL;
1844 for (i = 0; i < GD_rank(g)[r].n; i++) {
1845 v = GD_rank(g)[r].v[i];
1846 if (v == NULL) {
1847 fprintf(stderr, "NULL\t");
1848 if (!null_ok)
1849 abort();
1850 } else {
1851 fprintf(stderr, "%s(%f)\t", agnameof(v), ND_mval(v));
1852 assert(ND_rank(v) == r);
1853 assert(v != prev);
1854 prev = v;
1855 }
1856 }
1857 fprintf(stderr, "\n");
1858 }
1859}
1860
1861void check_order(void)
1862{
1863 int i, r;
1864 node_t *v;
1865 graph_t *g = Root;
1866
1867 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1868 assert(GD_rank(g)[r].v[GD_rank(g)[r].n] == NULL);
1869 for (i = 0; (v = GD_rank(g)[r].v[i]); i++) {
1870 assert(ND_rank(v) == r);
1871 assert(ND_order(v) == i);
1872 }
1873 }
1874}
1875#endif
1876
1878{
1879 char *p;
1880 double f;
1881
1882 /* set default values */
1883 MinQuit = 8;
1884 MaxIter = 24;
1885
1886 p = agget(g, "mclimit");
1887 if (p && (f = atof(p)) > 0.0) {
1888 MinQuit = MAX(1, scale_clamp(MinQuit, f));
1889 MaxIter = MAX(1, scale_clamp(MaxIter, f));
1890 }
1891}
1892
1893#ifdef DEBUG
1894void check_exchange(node_t * v, node_t * w)
1895{
1896 int i, r;
1897 node_t *u;
1898
1899 if (ND_clust(v) == NULL && ND_clust(w) == NULL)
1900 return;
1901 assert(ND_clust(v) == NULL || ND_clust(w) == NULL);
1902 assert(ND_rank(v) == ND_rank(w));
1903 assert(ND_order(v) < ND_order(w));
1904 r = ND_rank(v);
1905
1906 for (i = ND_order(v) + 1; i < ND_order(w); i++) {
1907 u = GD_rank(dot_root(v))[r].v[i];
1908 if (ND_clust(u))
1909 abort();
1910 }
1911}
1912
1913void check_vlists(graph_t * g)
1914{
1915 int c, i, j, r;
1916 node_t *u;
1917
1918 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1919 for (i = 0; i < GD_rank(g)[r].n; i++) {
1920 u = GD_rank(g)[r].v[i];
1921 j = ND_order(u);
1922 assert(GD_rank(Root)[r].v[j] == u);
1923 }
1924 if (GD_rankleader(g)) {
1925 u = GD_rankleader(g)[r];
1926 j = ND_order(u);
1927 assert(GD_rank(Root)[r].v[j] == u);
1928 }
1929 }
1930 for (c = 1; c <= GD_n_cluster(g); c++)
1931 check_vlists(GD_clust(g)[c]);
1932}
1933
1934void node_in_root_vlist(node_t * n)
1935{
1936 node_t **vptr;
1937
1938 for (vptr = GD_rank(Root)[ND_rank(n)].v; *vptr; vptr++)
1939 if (*vptr == n)
1940 break;
1941 if (*vptr == 0)
1942 abort();
1943}
1944#endif /* DEBUG code */
static agxbuf last
last message
Definition agerror.c:29
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
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 MIN(a, b)
Definition arith.h:28
abstract graph C library, Cgraph API
void class2(graph_t *g)
Definition class2.c:157
#define exchange(h, i, j)
Definition closest.c:85
bool mapbool(const char *p)
Definition utils.c:337
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:80
#define NORMAL
Definition const.h:24
#define FLATORDER
Definition const.h:28
#define NEW_RANK
Definition const.h:257
#define VIRTUAL
Definition const.h:25
#define CLUSTER
Definition const.h:40
#define REVERSED
Definition const.h:27
void decompose(graph_t *g, int pass)
Definition decomp.c:105
Agraph_t * dot_root(void *p)
Definition dotinit.c:525
bool is_cluster(Agraph_t *)
Definition rank.c:508
void flat_edge(Agraph_t *, Agedge_t *)
Definition fastgr.c:214
void merge_oneway(Agedge_t *, Agedge_t *)
Definition fastgr.c:288
Agedge_t * new_virtual_edge(Agnode_t *, Agnode_t *, Agedge_t *)
Definition fastgr.c:130
Agedge_t * find_flat_edge(Agnode_t *, Agnode_t *)
Definition fastgr.c:54
void delete_flat_edge(Agedge_t *)
Definition fastgr.c:221
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
int MaxIter
Definition globals.h:61
Agsym_t * G_ordering
Definition globals.h:71
Agsym_t * N_ordering
Definition globals.h:77
static bool Verbose
Definition gml2gv.c:23
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:198
int agnedges(Agraph_t *g)
Definition graph.c:163
int agdegree(Agraph_t *g, Agnode_t *n, int in, int out)
Definition graph.c:225
int agnnodes(Agraph_t *g)
Definition graph.c:157
char * agget(void *obj, char *name)
Definition attr.c:462
#define ED_to_orig(e)
Definition types.h:598
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:253
int agdeledge(Agraph_t *g, Agedge_t *arg_e)
Definition edge.c:327
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:71
#define ED_xpenalty(e)
Definition types.h:601
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:26
#define agtail(e)
Definition cgraph.h:988
#define ED_edge_type(e)
Definition types.h:582
#define ED_weight(e)
Definition types.h:603
#define aghead(e)
Definition cgraph.h:989
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:41
#define ED_head_port(e)
Definition types.h:588
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:57
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
void agwarningf(const char *fmt,...)
Definition agerror.c:173
void agerrorf(const char *fmt,...)
Definition agerror.c:165
#define GD_minrank(g)
Definition types.h:384
#define GD_maxrank(g)
Definition types.h:382
#define GD_clust(g)
Definition types.h:360
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:95
#define GD_flags(g)
Definition types.h:365
#define GD_rank(g)
Definition types.h:395
#define GD_has_flat_edges(g)
Definition types.h:370
#define GD_nlist(g)
Definition types.h:393
Agdesc_t Agstrictdirected
strict directed. A strict graph cannot have multi-edges or self-arcs.
Definition graph.c:273
#define GD_n_cluster(g)
Definition types.h:389
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:42
#define GD_comp(g)
Definition types.h:362
#define GD_flip(g)
Definition types.h:378
#define GD_rankleader(g)
Definition types.h:396
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:141
#define ND_rank(n)
Definition types.h:523
#define ND_prev(n)
Definition types.h:521
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:48
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:41
#define ND_has_port(n)
Definition types.h:495
#define ND_next(n)
Definition types.h:510
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:252
#define ND_clust(n)
Definition types.h:489
#define ND_other(n)
Definition types.h:514
#define ND_alg(n)
Definition types.h:484
#define ND_flat_out(n)
Definition types.h:493
#define ND_rw(n)
Definition types.h:525
#define ND_node_type(n)
Definition types.h:511
#define ND_lw(n)
Definition types.h:506
#define ND_mval(n)
Definition types.h:508
int agdelnode(Agraph_t *g, Agnode_t *arg_n)
removes a node from a graph or subgraph.
Definition node.c:190
#define ND_order(n)
Definition types.h:513
#define ND_UF_size(n)
Definition types.h:487
#define ND_weight_class(n)
Definition types.h:535
#define ND_low(n)
Definition types.h:505
#define ND_ranktype(n)
Definition types.h:524
#define ND_flat_in(n)
Definition types.h:492
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:143
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:233
Agraph_t * agroot(void *obj)
Definition obj.c:168
#define AGSEQ(obj)
Definition cgraph.h:225
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:89
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:73
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:78
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:53
static void indent(int ix)
Definition gv2gml.c:94
bool rm(Agraph_t *g)
Definition gv.cpp:584
Arithmetic helper functions.
static int scale_clamp(int original, double scale)
scale up or down a non-negative integer, clamping to [0, INT_MAX]
Definition gv_math.h:73
#define SWAP(a, b)
Definition gv_math.h:131
$2 u p prev
Definition htmlparse.y:297
rows row
Definition htmlparse.y:326
static double cross(double *u, double *v)
#define ITOS(i)
Definition itos.h:43
#define ND_onstack(n)
Definition acyclic.c:29
#define ND_mark(n)
Definition acyclic.c:28
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:73
int install_cluster(graph_t *g, node_t *n, int pass, node_queue_t *q)
Definition cluster.c:383
int expand_cluster(graph_t *subg)
Definition cluster.c:281
void mark_lowclusters(Agraph_t *root)
Definition cluster.c:403
#define DEFINE_LIST(name, type)
Definition list.h:22
#define neighbor(t, i, edim, elist)
Definition make_map.h:41
#define delta
Definition maze.c:136
#define isBackedge(e)
Definition mincross.c:233
static int betweenclust(edge_t *e)
Definition mincross.c:479
int build_ranks(graph_t *g, int pass, ints_t *scratch)
Definition mincross.c:1273
#define ND_hi(n)
Definition mincross.c:217
#define flatindex(v)
Definition mincross.c:86
static void free_matrix(adjmatrix_t *p)
Definition mincross.c:459
static bool ReMincross
Definition mincross.c:127
static bool flat_mval(node_t *n)
Definition mincross.c:1659
static int64_t mincross(graph_t *g, int startpass, ints_t *scratch)
Definition mincross.c:745
static bool inside_cluster(graph_t *g, node_t *v)
Definition mincross.c:958
#define ND_x(n)
Definition mincross.c:215
#define ORDINARY
Definition mincross.c:1794
static int64_t rcross(graph_t *g, int r, ints_t *Count)
Definition mincross.c:1575
static void init_mccomp(graph_t *g, size_t c)
Definition mincross.c:467
static void mincross_step(graph_t *g, int pass)
Definition mincross.c:1525
static int topsort(Agraph_t *g, Agraph_t *sg, Agnode_t **arr)
Definition mincross.c:246
static bool medians(graph_t *g, int r0, int r1)
Definition mincross.c:1691
static void reorder(graph_t *g, int r, bool reverse, bool hasfixed)
Definition mincross.c:1473
#define VAL(node, port)
Definition mincross.c:1689
static int edgeidcmpf(const void *, const void *)
Definition mincross.c:1771
static void flat_breakcycles(graph_t *g)
Definition mincross.c:1166
static void cleanup2(graph_t *g, int64_t nc)
Definition mincross.c:890
#define MARK(v)
Definition mincross.c:84
static bool is_a_normal_node_of(graph_t *g, node_t *v)
Definition mincross.c:942
static void save_best(graph_t *g)
Definition mincross.c:826
#define C_VS
Definition mincross.c:1800
static void init_mincross(graph_t *g)
Definition mincross.c:1079
#define ND_np(n)
Definition mincross.c:218
static int64_t transpose_step(graph_t *g, int r, bool reverse)
Definition mincross.c:687
static void fixLabelOrder(graph_t *g, rank_t *rk)
for each pair of nodes (labels), we add an edge
Definition mincross.c:288
void virtual_weight(edge_t *e)
Definition mincross.c:1819
static void merge2(graph_t *g)
Definition mincross.c:863
static node_t * furthestnode(graph_t *g, node_t *v, int dir)
Definition mincross.c:962
static int out_cross(node_t *v, node_t *w)
Definition mincross.c:655
static int ordercmpf(const void *, const void *)
Definition mincross.c:1638
void enqueue_neighbors(node_queue_t *q, node_t *n0, int pass)
Definition mincross.c:1349
static void do_ordering(graph_t *g, bool outflag)
Definition mincross.c:526
static void matrix_set(adjmatrix_t *me, size_t row, size_t col)
Definition mincross.c:67
void checkLabelOrder(graph_t *g)
Definition mincross.c:342
static int64_t mincross_clust(graph_t *g, ints_t *scratch)
Definition mincross.c:586
static int GlobalMinRank
Definition mincross.c:124
static const double Convergence
Definition mincross.c:121
static adjmatrix_t * new_matrix(size_t i, size_t j)
Definition mincross.c:452
#define SINGLETON
Definition mincross.c:1795
static Agraph_t * realFillRanks(Agraph_t *g, int rnks[], int rnks_sz, Agraph_t *sg)
Definition mincross.c:1032
static Agnode_t * findSource(Agraph_t *g, Agraph_t *sg)
Definition mincross.c:236
static int * TI_list
Definition mincross.c:126
void rec_save_vlists(graph_t *g)
Definition mincross.c:984
#define C_EE
Definition mincross.c:1799
static void do_ordering_node(graph_t *g, node_t *n, bool outflag)
Definition mincross.c:486
static int64_t in_cross(node_t *v, node_t *w)
Definition mincross.c:636
static graph_t * Root
Definition mincross.c:123
#define C_VV
Definition mincross.c:1802
static void flat_search(graph_t *g, node_t *v)
Definition mincross.c:1130
static int64_t ncross(ints_t *scratch)
Definition mincross.c:1620
static void ordered_edges(graph_t *g)
Definition mincross.c:559
static void transpose(graph_t *g, bool reverse)
Definition mincross.c:728
#define NTYPES
Definition mincross.c:1797
void rec_reset_vlists(graph_t *g)
Definition mincross.c:994
static void merge_components(graph_t *g)
Definition mincross.c:839
int dot_mincross(graph_t *g)
Definition mincross.c:381
static void mincross_options(graph_t *g)
Definition mincross.c:1877
static int endpoint_class(node_t *n)
Definition mincross.c:1810
static int getComp(graph_t *g, node_t *n, graph_t *comp, int *indices)
Definition mincross.c:265
static int GlobalMaxRank
Definition mincross.c:124
static bool left2right(graph_t *g, node_t *v, node_t *w)
Definition mincross.c:612
static int local_cross(elist l, int dir)
Definition mincross.c:1553
static void flat_reorder(graph_t *g)
Definition mincross.c:1402
#define C_SS
Definition mincross.c:1801
static void flat_rev(Agraph_t *g, Agedge_t *e)
Definition mincross.c:1103
static void emptyComp(graph_t *sg)
Definition mincross.c:222
static bool is_a_vnode_of_an_edge_of(graph_t *g, node_t *v)
Definition mincross.c:946
static void fillRanks(Agraph_t *g)
Definition mincross.c:1071
static void do_ordering_for_nodes(graph_t *g)
Definition mincross.c:535
static void postorder(graph_t *g, node_t *v, nodes_t *list, int r)
Definition mincross.c:1386
static int MinQuit
Definition mincross.c:120
static edge_t ** TE_list
Definition mincross.c:125
void allocate_ranks(graph_t *g)
Definition mincross.c:1197
#define ND_lo(n)
Definition mincross.c:216
static int table[NTYPES][NTYPES]
Definition mincross.c:1804
static void restore_best(graph_t *g)
Definition mincross.c:808
static bool matrix_get(adjmatrix_t *me, size_t row, size_t col)
Definition mincross.c:48
static bool constraining_flat_edge(Agraph_t *g, Agedge_t *e)
Definition mincross.c:1371
#define ND_idx(n)
Definition mincross.c:219
static int nodeposcmpf(const void *, const void *)
Definition mincross.c:1749
#define saveorder(v)
Definition mincross.c:85
void save_vlist(graph_t *g)
Definition mincross.c:974
int install_in_rank(graph_t *g, node_t *n)
Definition mincross.c:1225
#define VIRTUALNODE
Definition mincross.c:1796
#define M
Definition randomkit.c:90
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
Agobj_t base
Definition cgraph.h:269
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
graph or subgraph
Definition cgraph.h:424
Agraph_t * parent
Definition cgraph.h:433
implementation of Agrec_t
Definition cgraph.h:172
size_t nrows
Definition mincross.c:36
uint8_t * data
bit-packed backing memory
Definition mincross.c:38
size_t allocated
how many bytes have been allocated backing data?
Definition mincross.c:39
size_t ncols
Definition mincross.c:37
Definition types.h:251
edge_t ** list
Definition types.h:252
int hi
Definition mincross.c:211
Agrec_t h
Definition mincross.c:210
Agnode_t * np
Definition mincross.c:212
node_t ** v
Definition types.h:202
int n
Definition types.h:201
double elapsed_sec(void)
Definition timing.c:48
void start_timer(void)
Definition timing.c:43
#define elist_append(item, L)
Definition types.h:261
#define alloc_elist(n, L)
Definition types.h:267
Definition grammar.c:89
#define MAX(a, b)
Definition write.c:32