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