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