static void dot_cleanup_graph(graph_t * g) { int i, c; graph_t *clust; for (c = 1; c <= GD_n_cluster(g); c++) { clust = GD_clust(g)[c]; GD_cluster_was_collapsed(clust) = FALSE; dot_cleanup(clust); } if (GD_clust(g)) free (GD_clust(g)); if (GD_rankleader(g)) free (GD_rankleader(g)); free_list(GD_comp(g)); if (GD_rank(g)) { for (i = GD_minrank(g); i <= GD_maxrank(g); i++) free(GD_rank(g)[i].av); if (GD_minrank(g) == -1) free(GD_rank(g)-1); else free(GD_rank(g)); } if (g != agroot(g)) #ifndef WITH_CGRAPH memset(&(g->u), 0, sizeof(Agraphinfo_t)); #else /* WITH_CGRAPH */ agclean(g,AGRAPH,"Agraphinfo_t"); #endif /* WITH_CGRAPH */ }
int transpose_onerank(Agraph_t* g, int r, boolean reverse) { int i,c0,c1,rv; node_t *v,*w; rv = 0; GD_rank(g)[r].candidate = FALSE; for (i = leftmost(g,r); i < rightmost(g,r); i++) { v = GD_rank(g)[r].v[i]; w = GD_rank(g)[r].v[i+1]; assert (ND_order(v) < ND_order(w)); if (left2right(g,v,w)) continue; c0 = c1 = 0; if (r > GD_minrank(g)) { c0 += in_cross(v,w); c1 += in_cross(w,v); } if (r < GD_maxrank(g)) { c0 += out_cross(v,w); c1 += out_cross(w,v); } if ((c1 < c0) || ((c0 > 0) && reverse && (c1 == c0))) { exchange(g,v,w); rv += (c0 - c1); } } return rv; }
/* sets ports that represent connections to subclusters */ static void subclustports(Agraph_t *ug) { Agraph_t *model, *clustmodel; Agnode_t *x; Agedge_t *e; vpath_t *p; Dict_t *d; double frac; /* walk all the paths */ model = GD_model(ug); d = repdict(model); for (p = dtfirst(d); p; p = dtnext(d,p)) { if ((ND_type(p->v[p->low])) == NODETYPE_CNODE) { x = p->v[p->low]; clustmodel = GD_model(ND_cluster(x)); frac = (ND_order(x) + 1) / GD_rank(clustmodel)[ND_rank(x)].n; e = p->e[p->low]; ED_tailport(e).p.x = 2 * PORTCLAMP * (frac - 0.5) + p->tailport; } if ((ND_type(p->v[p->high])) == NODETYPE_CNODE) { x = p->v[p->high]; clustmodel = GD_model(ND_cluster(x)); frac = (ND_order(x) + 1) / GD_rank(clustmodel)[ND_rank(x)].n; e = p->e[p->high-1]; ED_headport(e).p.x = 2 * PORTCLAMP * (frac - 0.5) + p->headport; } } }
/* * defines ND_sortweight of each node in r0 w.r.t. r1 * returns... */ static boolean medians(Agraph_t *g, int r0, int r1) { static int *list; static int list_extent; int i,j,lm,rm,lspan,rspan; node_t *n,**v; edge_t *e; boolean hasfixed = FALSE; if (list_extent < GD_maxinoutdeg(g->root)) { list_extent = GD_maxinoutdeg(g->root); if (!list) list = realloc(list,sizeof(list[0])*list_extent); else list = realloc(list,sizeof(list[0])*list_extent); } v = GD_rank(g)[r0].v; for (i = leftmost(g,r0); i <= rightmost(g,r0); i++) { n = v[i]; j = 0; if (r1 > r0) for (e = agfstout(g,n); e; e = agnxtout(g,e)) {if (ED_xpenalty(e) > 0) list[j++] = VAL(e->head,ED_headport(e));} else for (e = agfstin(g,n); e; e = agnxtin(g,e)) {if (ED_xpenalty(e) > 0) list[j++] = VAL(e->tail,ED_tailport(e));} switch(j) { case 0: ND_sortweight(n) = -1; /* no neighbor - median undefined */ break; case 1: ND_sortweight(n) = list[0]; break; case 2: ND_sortweight(n) = (list[0] + list[1])/2; break; default: qsort(list,j,sizeof(int),int_cmpf); if (j % 2) ND_sortweight(n) = list[j/2]; else { /* weighted median */ rm = j/2; lm = rm - 1; rspan = list[j-1] - list[rm]; lspan = list[lm] - list[0]; if (lspan == rspan) ND_sortweight(n) = (list[lm] + list[rm])/2; else { int w = list[lm]*rspan + list[rm]*lspan; ND_sortweight(n) = w / (lspan + rspan); } } } } #ifdef NOTDEF /* this code was in the old mincross */ for (i = 0; i < GD_rank(g)[r0].n; i++) { n = v[i]; if ((ND_out(n).size == 0) && (ND_in(n).size == 0)) hasfixed |= flat_sortweight(n); } #endif return hasfixed; }
static void rebuild_vlists(graph_t * g) { int c, i, r, maxi; node_t *n, *lead; edge_t *e, *rep; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) GD_rankleader(g)[r] = NULL; for (n = agfstnode(g); n; n = agnxtnode(g, n)) { infuse(g, n); for (e = agfstout(g, n); e; e = agnxtout(g, e)) { for (rep = e; ED_to_virt(rep); rep = ED_to_virt(rep)); while (ND_rank(rep->head) < ND_rank(e->head)) { infuse(g, rep->head); rep = ND_out(rep->head).list[0]; } } } for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { lead = GD_rankleader(g)[r]; if (ND_rank(g->root)[r].v[ND_order(lead)] != lead) abort(); GD_rank(g)[r].v = ND_rank(g->root)[r].v + GD_rankleader(g)[r]->u.order; maxi = -1; for (i = 0; i < GD_rank(g)[r].n; i++) { if ((n = GD_rank(g)[r].v[i]) == NULL) break; if (ND_node_type(n) == NORMAL) { if (agcontains(g, n)) maxi = i; else break; } else { edge_t *e; for (e = ND_in(n).list[0]; e && ED_to_orig(e); e = ED_to_orig(e)); if (e && (agcontains(g, e->tail)) && agcontains(g, e->head)) maxi = i; } } if (maxi == -1) agerr(AGWARN, "degenerate concentrated rank %s,%d\n", g->name, r); GD_rank(g)[r].n = maxi + 1; } for (c = 1; c <= GD_n_cluster(g); c++) rebuild_vlists(GD_clust(g)[c]); }
static void transpose_sweep(Agraph_t* g, int reverse) { int r,delta; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) GD_rank(g)[r].candidate = TRUE; do { delta = 0; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) if (GD_rank(g)[r].candidate) delta += transpose_onerank(g,r,reverse); } while (delta >= 1); /* while (delta > crossings(g)*(1.0 - Convergence));*/ }
static void presort(Agraph_t *ug) { int r; int i; Agraph_t *mg; if (ug == ug->root) return; mg = GD_model(ug); for (r = GD_minrank(mg); r <= GD_maxrank(mg); r++) { qsort(GD_rank(mg)[r].v,GD_rank(mg)[r].n,sizeof(Agnode_t*),presort_cmpf); for (i = leftmost(mg,r); i < rightmost(mg,r); i++) ND_order(GD_rank(mg)[r].v[i]) = i; } }
static void globalopt(Agraph_t *root) { Agraph_t *g; rank_t *glob; g = GD_model(root); glob = globalize(root,g); GD_rank(g) = glob; fprintf(stderr,"%s: %d crossings\n",root->name,crossings(g)); }
static void install(Agraph_t *g, Agnode_t *n) { int rank; rank_t *r; rank = ND_rank(n); r = &GD_rank(g)[rank]; r->v[r->n] = n; ND_order(n) = r->n++; }
static void restorebest(graph_t *g) { Agnode_t *n; int i; for (i = GD_minrank(g); i <= GD_maxrank(g); i++) GD_rank(g)[i].changed = FALSE; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (ND_order(n) != ND_saveorder(n)) { invalidate(g,ND_rank(n)); GD_rank(g)[i].changed = TRUE; ND_order(n) = ND_saveorder(n); } } for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { if (GD_rank(g)[i].changed) qsort(GD_rank(g)[i].v,GD_rank(g)[i].n,sizeof(Agnode_t*),ND_order_cmpf); } }
static void dot_cleanup_graph(graph_t * g) { int i, c; graph_t *clust; for (c = 1; c <= GD_n_cluster(g); c++) { clust = GD_clust(g)[c]; GD_cluster_was_collapsed(clust) = FALSE; dot_cleanup(clust); } free_list(GD_comp(g)); if ((g == g->root) && GD_rank(g)) { for (i = GD_minrank(g); i <= GD_maxrank(g); i++) free(GD_rank(g)[i].v); free(GD_rank(g)); } if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t)); }
static void mergevirtual(graph_t * g, int r, int lpos, int rpos, int dir) { int i, k; node_t *left, *right; edge_t *e, *f, *e0; left = GD_rank(g)[r].v[lpos]; /* merge all right nodes into the leftmost one */ for (i = lpos + 1; i <= rpos; i++) { right = GD_rank(g)[r].v[i]; if (dir == DOWN) { while ((e = ND_out(right).list[0])) { for (k = 0; (f = ND_out(left).list[k]); k++) if (f->head == e->head) break; if (f == NULL) f = virtual_edge(left, e->head, e); while ((e0 = ND_in(right).list[0])) { merge_oneway(e0, f); /*ED_weight(f) += ED_weight(e0); */ delete_fast_edge(e0); } delete_fast_edge(e); } } else { while ((e = ND_in(right).list[0])) { for (k = 0; (f = ND_in(left).list[k]); k++) if (f->tail == e->tail) break; if (f == NULL) f = virtual_edge(e->tail, left, e); while ((e0 = ND_out(right).list[0])) { merge_oneway(e0, f); delete_fast_edge(e0); } delete_fast_edge(e); } } assert(ND_in(right).size + ND_out(right).size == 0); delete_fast_node(g, right); } k = lpos + 1; i = rpos + 1; while (i < GD_rank(g)[r].n) { node_t *n; n = GD_rank(g)[r].v[k] = GD_rank(g)[r].v[i]; ND_order(n) = k; k++; i++; } GD_rank(g)[r].n = k; GD_rank(g)[r].v[k] = NULL; }
void dot_concentrate(graph_t * g) { int c, r, leftpos, rightpos; node_t *left, *right; if (GD_maxrank(g) - GD_minrank(g) <= 1) return; /* this is the downward looking pass. r is a candidate rank. */ for (r = 1; GD_rank(g)[r + 1].n; r++) { for (leftpos = 0; leftpos < GD_rank(g)[r].n; leftpos++) { left = GD_rank(g)[r].v[leftpos]; if (downcandidate(left) == FALSE) continue; for (rightpos = leftpos + 1; rightpos < GD_rank(g)[r].n; rightpos++) { right = GD_rank(g)[r].v[rightpos]; if (bothdowncandidates(left, right) == FALSE) break; } if (rightpos - leftpos > 1) mergevirtual(g, r, leftpos, rightpos - 1, DOWN); } } /* this is the corresponding upward pass */ while (r > 0) { for (leftpos = 0; leftpos < GD_rank(g)[r].n; leftpos++) { left = GD_rank(g)[r].v[leftpos]; if (upcandidate(left) == FALSE) continue; for (rightpos = leftpos + 1; rightpos < GD_rank(g)[r].n; rightpos++) { right = GD_rank(g)[r].v[rightpos]; if (bothupcandidates(left, right) == FALSE) break; } if (rightpos - leftpos > 1) mergevirtual(g, r, leftpos, rightpos - 1, UP); } r--; } for (c = 1; c <= GD_n_cluster(g); c++) rebuild_vlists(GD_clust(g)[c]); }
static void dot_cleanup_graph(graph_t * g) { int i; graph_t *subg; for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) { dot_cleanup_graph(subg); } if (GD_clust(g)) free (GD_clust(g)); if (GD_rankleader(g)) free (GD_rankleader(g)); free_list(GD_comp(g)); if (GD_rank(g)) { for (i = GD_minrank(g); i <= GD_maxrank(g); i++) free(GD_rank(g)[i].av); if (GD_minrank(g) == -1) free(GD_rank(g)-1); else free(GD_rank(g)); } if (g != agroot(g)) agdelrec(g,"Agraphinfo_t"); }
static int left2right(Agraph_t *g, node_t *v, node_t *w) { int rv; #ifdef NOTDEF adjmatrix_t *M; M = GD_rank(g)[ND_rank(v)].flat; if (M == NULL) rv = FALSE; else { if (GD_flip(g)) {node_t *t = v; v = w; w = t;} rv = ELT(M,flatindex(v),flatindex(w)); } #else rv = FALSE; #endif return rv; }
/* swaps two nodes in the same level */ static void exchange(Agraph_t *g, Agnode_t *u, Agnode_t *v) { rank_t *r; int ui,vi,rank; assert(ND_rank(u) == ND_rank(v)); rank = ND_rank(u); r = &GD_rank(g)[rank]; ui = ND_order(u); vi = ND_order(v); ND_order(v) = ui; ND_order(u) = vi; r->v[ND_order(u)] = u; r->v[ND_order(v)] = v; r->crossing_cache.valid = FALSE; r->changed = TRUE; r->candidate = TRUE; /* old dot had this. i have qualms. sn */ invalidate(g,rank); }
static void dumpRanks (graph_t * g) { int i, j; node_t* u; rank_t *rank = GD_rank(g); int rcnt = 0; for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { fprintf (stderr, "[%d] :", i); for (j = 0; j < rank[i].n; j++) { u = rank[i].v[j]; rcnt++; if (streq(u->name,"virtual")) fprintf (stderr, " %x", u); else fprintf (stderr, " %s", u->name); } fprintf (stderr, "\n"); } fprintf (stderr, "count %d rank count = %d\n", fastn(g), rcnt); }
static void remove_from_rank (Agraph_t * g, Agnode_t* n) { Agnode_t* v = NULL; int j, rk = ND_rank(n); for (j = 0; j < GD_rank(g)[rk].n; j++) { v = GD_rank(g)[rk].v[j]; if (v == n) { for (j++; j < GD_rank(g)[rk].n; j++) { GD_rank(g)[rk].v[j-1] = GD_rank(g)[rk].v[j]; } GD_rank(g)[rk].n--; break; } } assert (v == n); /* if found */ }
static void reorder(graph_t *g, int r, boolean reverse, boolean hasfixed) { boolean changed, muststay; node_t **vlist, **lp, **rp, **ep; int i; changed = FALSE; vlist = GD_rank(g)[r].v; ep = &vlist[rightmost(g,r)]; for (i = leftmost(g,r); i <= rightmost(g,r); i++) { lp = &vlist[leftmost(g,r)]; /* find leftmost node that can be compared */ while ((lp < ep) && (ND_sortweight(*lp) < 0)) lp++; if (lp >= ep) break; /* find the node that can be compared */ muststay = FALSE; for (rp = lp + 1; rp < ep; rp++) { if (left2right(g,*lp,*rp)) { muststay = TRUE; break; } if (ND_sortweight(*rp) >= 0) break; /* weight defined; it's comparable */ } if (rp >= ep) break; if (muststay == FALSE) { register int p1 = ND_sortweight(*lp); register int p2 = ND_sortweight(*rp); if ((p1 > p2) || ((p1 == p2) && (reverse))) { exchange(g,*lp,*rp); changed = TRUE; } } lp = rp; if ((hasfixed == FALSE) && (reverse == FALSE)) ep--; } if (changed) { GD_rank(g)[r].changed = TRUE; GD_rank(g)[r].crossing_cache.valid = FALSE; if (r > 0) GD_rank(g)[r-1].crossing_cache.valid = FALSE; if (r + 1 > GD_rank(g)[r+1].n) GD_rank(g)[r-1].crossing_cache.valid = FALSE; } }
static void invalidate(Agraph_t *g, int rank) { if (rank > GD_minrank(g)) GD_rank(g)[rank-1].crossing_cache.valid = FALSE; if (rank > GD_minrank(g)) GD_rank(g)[rank-1].candidate = TRUE; if (rank < GD_maxrank(g)) GD_rank(g)[rank+1].candidate = TRUE; }
static void resetNodeCountOnRanks(Agraph_t * g) { int i; for (i = GD_minrank(g); i <= GD_maxrank(g); i++) GD_rank(g)[i].n = 0; }
static void merge_ranks(graph_t * subg) { int i, d, r, pos, ipos; node_t *v; graph_t *root; root = agroot(subg); if (GD_minrank(subg) > 0) #ifndef WITH_CGRAPH ND_rank(root)[GD_minrank(subg) - 1].valid = FALSE; #else /* WITH_CGRAPH */ GD_rank(root)[GD_minrank(subg) - 1].valid = FALSE; #endif /* WITH_CGRAPH */ for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) { d = GD_rank(subg)[r].n; #ifndef WITH_CGRAPH ipos = pos = GD_rankleader(subg)[r]->u.order; #else /* WITH_CGRAPH */ ipos = pos = ND_order(GD_rankleader(subg)[r]); #endif /* WITH_CGRAPH */ make_slots(root, r, pos, d); for (i = 0; i < GD_rank(subg)[r].n; i++) { #ifndef WITH_CGRAPH v = ND_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i]; #else /* WITH_CGRAPH */ v = GD_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i]; #endif /* WITH_CGRAPH */ ND_order(v) = pos++; #ifndef WITH_CGRAPH v->graph = subg->root; #else /* WITH_CGRAPH */ /* real nodes automatically have v->root = root graph */ if (ND_node_type(v) == VIRTUAL) v->root = root; #endif /* WITH_CGRAPH */ delete_fast_node(subg, v); fast_node(agroot(subg), v); GD_n_nodes(agroot(subg))++; } #ifndef WITH_CGRAPH GD_rank(subg)[r].v = ND_rank(root)[r].v + ipos; ND_rank(root)[r].valid = FALSE; #else /* WITH_CGRAPH */ GD_rank(subg)[r].v = GD_rank(root)[r].v + ipos; GD_rank(root)[r].valid = FALSE; #endif /* WITH_CGRAPH */ } if (r < GD_maxrank(root)) GD_rank(root)[r].valid = FALSE; GD_expanded(subg) = TRUE; }
/* make d slots starting at position pos (where 1 already exists) */ static void make_slots(graph_t * root, int r, int pos, int d) { int i; node_t *v, **vlist; #ifndef WITH_CGRAPH vlist = ND_rank(root)[r].v; #else /* WITH_CGRAPH */ vlist = GD_rank(root)[r].v; #endif /* WITH_CGRAPH */ if (d <= 0) { #ifndef WITH_CGRAPH for (i = pos - d + 1; i < ND_rank(root)[r].n; i++) { #else /* WITH_CGRAPH */ for (i = pos - d + 1; i < GD_rank(root)[r].n; i++) { #endif /* WITH_CGRAPH */ v = vlist[i]; ND_order(v) = i + d - 1; vlist[ND_order(v)] = v; } #ifndef WITH_CGRAPH for (i = ND_rank(root)[r].n + d - 1; i < ND_rank(root)[r].n; i++) #else /* WITH_CGRAPH */ for (i = GD_rank(root)[r].n + d - 1; i < GD_rank(root)[r].n; i++) #endif /* WITH_CGRAPH */ vlist[i] = NULL; } else { /*assert(ND_rank(root)[r].n + d - 1 <= ND_rank(root)[r].an);*/ #ifndef WITH_CGRAPH for (i = ND_rank(root)[r].n - 1; i > pos; i--) { #else /* WITH_CGRAPH */ for (i = GD_rank(root)[r].n - 1; i > pos; i--) { #endif /* WITH_CGRAPH */ v = vlist[i]; ND_order(v) = i + d - 1; vlist[ND_order(v)] = v; } for (i = pos + 1; i < pos + d; i++) vlist[i] = NULL; } #ifndef WITH_CGRAPH ND_rank(root)[r].n += d - 1; #else /* WITH_CGRAPH */ GD_rank(root)[r].n += d - 1; #endif /* WITH_CGRAPH */ } static node_t* clone_vn(graph_t * g, node_t * vn) { node_t *rv; int r; r = ND_rank(vn); make_slots(g, r, ND_order(vn), 2); rv = virtual_node(g); ND_lw(rv) = ND_lw(vn); ND_rw(rv) = ND_rw(vn); ND_rank(rv) = ND_rank(vn); ND_order(rv) = ND_order(vn) + 1; GD_rank(g)[r].v[ND_order(rv)] = rv; return rv; }
static int rightmost(Agraph_t *model, int r) {return GD_rank(model)[r].n - 1;}