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