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