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