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