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