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