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