/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ #pragma prototyped /* * dot_mincross(g) takes a ranked graphs, and finds an ordering * that avoids edge crossings. clusters are expanded. * N.B. the rank structure is global (not allocated per cluster) * because mincross may compare nodes in different clusters. */ #include "dot.h" /* #define DEBUG */ #define MARK(v) ((v)->u.mark) #define saveorder(v) ((v)->u.coord.x) #define flatindex(v) ((v)->u.low) /* forward declarations */ static boolean medians(graph_t *g, int r0, int r1); static int nodeposcmpf(node_t **n0, node_t **n1); static int edgeidcmpf(edge_t **e0, edge_t **e1); static void flat_breakcycles(graph_t* g); static void flat_reorder(graph_t* g); static void flat_search(graph_t* g, node_t* v); static void init_mincross(graph_t* g); static void merge2(graph_t* g); static void init_mccomp(graph_t* g, int c); static void cleanup2(graph_t* g, int nc); static int mincross_clust(graph_t *par, graph_t *g); static int mincross(graph_t *g, int startpass, int endpass); static void init_mincross(graph_t* g); static void mincross_step(graph_t* g, int pass); static void mincross_options(graph_t* g); static void save_best(graph_t* g); static void restore_best(graph_t* g); static adjmatrix_t* new_matrix(int i, int j); static void free_matrix(adjmatrix_t* p); #ifdef DEBUG void check_rs(graph_t* g, int null_ok); void check_order(void); void check_vlists(graph_t* g); void node_in_root_vlist(node_t* n); #endif /* mincross parameters */ static int MinQuit; static double Convergence; static graph_t *Root; static int GlobalMinRank,GlobalMaxRank; static edge_t **TE_list; static int *TI_list; static boolean ReMincross; void dot_mincross(graph_t* g) { int c,nc; char *s; init_mincross(g); for (nc = c = 0; c < GD_comp(g).size; c++) { init_mccomp(g,c); nc += mincross(g,0,2); } merge2(g); /* run mincross on contents of each cluster */ for (c = 1; c <= GD_n_cluster(g); c++) { nc += mincross_clust(g,GD_clust(g)[c]); #ifdef DEBUG check_vlists(GD_clust(g)[c]); check_order(); #endif } if ((GD_n_cluster(g) > 0) && (!(s = agget(g,"remincross")) || (mapbool(s)))) { mark_lowclusters(g); ReMincross = TRUE; nc = mincross(g,2,2); #ifdef DEBUG for (c = 1; c <= GD_n_cluster(g); c++) check_vlists(GD_clust(g)[c]); #endif } cleanup2(g,nc); } static adjmatrix_t* new_matrix(int i, int j) { adjmatrix_t *rv = NEW(adjmatrix_t); rv->nrows = i; rv->ncols = j; rv->data = N_NEW(i*j,char); return rv; } static void free_matrix(adjmatrix_t* p) { if (p) {free(p->data); free(p);} } #define ELT(M,i,j) (M->data[((i)*M->ncols)+(j)]) static void init_mccomp(graph_t* g, int c) { int r; GD_nlist(g) = GD_comp(g).list[c]; if (c > 0) { for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { GD_rank(g)[r].v = GD_rank(g)[r].v + GD_rank(g)[r].n; GD_rank(g)[r].n = 0; } } } static int mincross_clust(graph_t *par, graph_t *g) { int c,nc; expand_cluster(g); ordered_edges(g); flat_breakcycles(g); flat_reorder(g); nc = mincross(g,2,2); for (c = 1; c <= GD_n_cluster(g); c++) nc += mincross_clust(g,GD_clust(g)[c]); save_vlist(g); return nc; } static int mincross(graph_t *g, int startpass, int endpass) { int maxthispass,iter,trying,pass; int cur_cross,best_cross; if (startpass > 1) { cur_cross = best_cross = ncross(g); save_best(g); } else cur_cross = best_cross = MAXINT; for (pass = startpass; pass <= endpass; pass++) { if (pass <= 1) { maxthispass = MIN(4,MaxIter); if (g == g->root) build_ranks(g,pass); if (pass == 0) flat_breakcycles(g); flat_reorder(g); if ((cur_cross = ncross(g)) <= best_cross) { save_best(g); best_cross = cur_cross; } trying = 0; } else { maxthispass = MaxIter; if (cur_cross > best_cross) restore_best(g); cur_cross = best_cross; } trying = 0; for (iter = 0; iter < maxthispass; iter++) { if (Verbose) fprintf(stderr,"mincross: pass %d iter %d trying %d cur_cross %d best_cross %d\n",pass,iter,trying,cur_cross,best_cross); if (trying++ >= MinQuit) break; if (cur_cross == 0) break; mincross_step(g,iter); if ((cur_cross = ncross(g)) <= best_cross) { save_best(g); if (cur_cross < Convergence*best_cross) trying = 0; best_cross = cur_cross; } } if (cur_cross == 0) break; } if (cur_cross > best_cross) restore_best(g); if (best_cross > 0) {transpose(g,FALSE); best_cross= ncross(g);} return best_cross; } static void restore_best(graph_t* g) { node_t *n; int r; for (n = GD_nlist(g); n; n = ND_next(n)) ND_order(n) = saveorder(n); for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { GD_rank(Root)[r].valid = FALSE; qsort(GD_rank(g)[r].v,GD_rank(g)[r].n,sizeof(GD_rank(g)[0].v[0]),(qsort_cmpf)nodeposcmpf); } } static void save_best(graph_t* g) { node_t *n; for (n = GD_nlist(g); n; n = ND_next(n)) saveorder(n) = ND_order(n); } /* merge connected components, create globally consistent rank lists */ static void merge2(graph_t* g) { int i,r; node_t *v; /* merge the components and rank limits */ merge_components(g); /* install complete ranks */ for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { GD_rank(g)[r].n = GD_rank(g)[r].an; GD_rank(g)[r].v = GD_rank(g)[r].av; for (i = 0; i < GD_rank(g)[r].n; i++) { v = GD_rank(g)[r].v[i]; if (v == NULL) { if (Verbose) fprintf(stderr,"merge2: graph %s, rank %d has only %d < %d nodes\n",g->name,r,i,GD_rank(g)[r].n); GD_rank(g)[r].n = i; break; } ND_order(v) = i; } } } static void cleanup2(graph_t* g, int nc) { int i,j,r,c; node_t *v; edge_t *e; if (TI_list) {free(TI_list); TI_list=NULL;} if (TE_list) {free(TE_list); TE_list=NULL;} /* fix vlists of clusters */ for (c = 1; c <= GD_n_cluster(g); c++) rec_reset_vlists(GD_clust(g)[c]); /* remove node temporary edges for ordering nodes */ for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { for (i = 0; i < GD_rank(g)[r].n; i++) { v = GD_rank(g)[r].v[i]; ND_order(v) = i; if (ND_flat_out(v).list) { for (j = 0; (e = ND_flat_out(v).list[j]); j++) if (ED_edge_type(e) == FLATORDER) { delete_flat_edge(e); free(e); j--; } } } free_matrix(GD_rank(g)[r].flat); } if (Verbose) fprintf(stderr,"mincross %s: %d crossings, %.2f secs.\n", g->name,nc,elapsed_sec()); } static node_t* neighbor(node_t* v, int dir) { node_t *rv; rv = NULL; if (dir < 0) { if (ND_order(v) > 0) rv = GD_rank(Root)[ND_rank(v)].v[ND_order(v) - 1]; } else rv = GD_rank(Root)[ND_rank(v)].v[ND_order(v) + 1]; return rv; } int inside_cluster(graph_t* g, node_t* v) { return (is_a_normal_node_of(g,v) | is_a_vnode_of_an_edge_of(g,v)); } int is_a_normal_node_of(graph_t* g, node_t* v) { return ((ND_node_type(v) == NORMAL) && agcontains(g,v)); } int is_a_vnode_of_an_edge_of(graph_t* g, node_t* v) { if ((ND_node_type(v) == VIRTUAL) && (ND_in(v).size == 1) && (ND_out(v).size == 1) ) { edge_t *e = ND_out(v).list[0]; while (ED_edge_type(e) != NORMAL) e = ED_to_orig(e); if (agcontains(g,e)) return TRUE; } return FALSE; } static node_t *furthestnode(graph_t* g, node_t* v, int dir) { node_t *u,*rv; rv = u = v; while ((u = neighbor(u,dir))) { if (is_a_normal_node_of(g,u)) rv = u; else if (is_a_vnode_of_an_edge_of(g,u)) rv = u; } return rv; } void save_vlist(graph_t* g) { int r; if (GD_rankleader(g)) for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { GD_rankleader(g)[r] = GD_rank(g)[r].v[0]; } } void rec_save_vlists(graph_t* g) { int c; save_vlist(g); for (c = 1; c <= GD_n_cluster(g); c++) rec_save_vlists(GD_clust(g)[c]); } void rec_reset_vlists(graph_t* g) { int r,c; node_t *u,*v,*w; /* fix vlists of sub-clusters */ for (c = 1; c <= GD_n_cluster(g); c++) rec_reset_vlists(GD_clust(g)[c]); if (GD_rankleader(g)) for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { v = GD_rankleader(g)[r]; #ifdef DEBUG node_in_root_vlist(v); #endif u = furthestnode(g,v,-1); w = furthestnode(g,v, 1); GD_rankleader(g)[r] = u; #ifdef DEBUG assert(GD_rank(g->root)[r].v[ND_order(u)] == u); #endif GD_rank(g)[r].v = ND_rank(g->root)[r].v + ND_order(u); GD_rank(g)[r].n = ND_order(w) - ND_order(u) + 1; } } static void init_mincross(graph_t* g) { if (Verbose) start_timer(); ReMincross = FALSE; Root = g; TE_list = N_NEW(agnedges(g->root),edge_t*); TI_list = N_NEW(agnedges(g->root),int); mincross_options(g); class2(g); decompose(g,1); allocate_ranks(g); ordered_edges(g); GlobalMinRank = GD_minrank(g); GlobalMaxRank = GD_maxrank(g); } static void flat_search(graph_t* g, node_t* v) { int i,j; boolean hascl; edge_t *e,*rev; adjmatrix_t *M = GD_rank(g)[ND_rank(v)].flat; ND_mark(v) = TRUE; ND_onstack(v) = TRUE; hascl = (ND_n_cluster(g->root) > 0); if (ND_flat_out(v).list) for (i = 0; (e = ND_flat_out(v).list[i]); i++) { if (hascl && NOT(agcontains(g,e->tail) && agcontains(g,e->head))) continue; if (ED_weight(e) == 0) continue; if (ND_onstack(e->head) == TRUE) { assert(flatindex(e->head) < M->nrows); assert(flatindex(e->tail) < M->ncols); ELT(M,flatindex(e->head),flatindex(e->tail)) = 1; delete_flat_edge(e); i--; if (ED_edge_type(e) == FLATORDER) continue; for (j = 0; (rev = ND_flat_out(e->head).list[j]); j++) if (rev->head == e->tail) break; if (rev) { merge_oneway(e,rev); if (ED_to_virt(e) == 0) ED_to_virt(e) = rev; if ((ED_edge_type(rev) == FLATORDER) && (ED_to_orig(rev) == 0)) ED_to_orig(rev) = e; elist_append(e,ND_other(e->tail)); } else { rev = new_virtual_edge(e->head,e->tail,e); rev->u.label = e->u.label; /* SCN hack */ ED_edge_type(rev) = REVERSED; flat_edge(g,rev); } } else { assert(flatindex(e->head) < M->nrows); assert(flatindex(e->tail) < M->ncols); ELT(M,flatindex(e->tail),flatindex(e->head)) = 1; if (ND_mark(e->head) == FALSE) flat_search(g,e->head); } } ND_onstack(v) = FALSE; } static void flat_breakcycles(graph_t* g) { int i,r,flat; node_t *v; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { flat = 0; for (i = 0; i < GD_rank(g)[r].n; i++) { v = GD_rank(g)[r].v[i]; ND_mark(v) = ND_onstack(v) = FALSE; flatindex(v) = i; if ((ND_flat_out(v).size > 0) && (flat == 0)) { GD_rank(g)[r].flat = new_matrix(GD_rank(g)[r].n,GD_rank(g)[r].n); flat = 1; } } if (flat) { for (i = 0; i < GD_rank(g)[r].n; i++) { v = GD_rank(g)[r].v[i]; if (ND_mark(v) == FALSE) flat_search(g,v); } } } } int left2right(graph_t *g, node_t *v, node_t *w) { adjmatrix_t *M; int rv; /* CLUSTER indicates orig nodes of clusters, and vnodes of skeletons */ if (ReMincross == FALSE) { if ((ND_clust(v) != ND_clust(w)) && (ND_clust(v)) && (ND_clust(w))) { /* the following allows cluster skeletons to be swapped */ if ((ND_ranktype(v) == CLUSTER) && (ND_node_type(v) == VIRTUAL)) return FALSE; if ((ND_ranktype(w) == CLUSTER) && (ND_node_type(w) == VIRTUAL)) return FALSE; return TRUE; /*return ((ND_ranktype(v) != CLUSTER) && (ND_ranktype(w) != CLUSTER));*/ } } else { if ((ND_clust(v)) != (ND_clust(w))) return TRUE; } M = GD_rank(g)[ND_rank(v)].flat; if (M == NULL) rv = FALSE; else { if (GD_left_to_right(g)) {node_t *t = v; v = w; w = t;} rv = ELT(M,flatindex(v),flatindex(w)); } return rv; } static void clust_count_ranks(graph_t* g, int* count) { node_t *n; edge_t *e; int low,high,j; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { assert (ND_UF_size(n) > 0); count[ND_rank(n)] += ND_UF_size(n); for (e = agfstout(g->root,n); e; e = agnxtout(g->root,e)) { low = ND_rank(e->tail); high = ND_rank(e->head); if (low > high) {int t = low; low = high; high = t;} assert(low <= high); for (j = low + 1; j <= high - 1; j++) count[j] += ED_xpenalty(e); } } } /* should combine with previous, just need to count rankleaders */ static void count_ranks(graph_t* g, int** c0) { static int *count; int c; node_t *n; edge_t *e; int low,high,i,j; count = ALLOC(GD_maxrank(Root)+1,count,int); for (c = 0; c <= GD_maxrank(g); c++) count[c] = 0; for (c = 0; c < GD_comp(g).size; c++) { for (n = GD_comp(g).list[c]; n; n = ND_next(n)) { assert (ND_UF_size(n) > 0); count[ND_rank(n)] += ND_UF_size(n); for (i = 0; (e = ND_out(n).list[i]); i++) { low = ND_rank(e->tail); high = ND_rank(e->head); assert(low < high); for (j = low + 1; j <= high - 1; j++) count[j] += ED_xpenalty(e); } } } for (c = 1; c <= GD_n_cluster(g); c++) clust_count_ranks(GD_clust(g)[c],count); *c0 = count; } /* allocates ranks, with enough space for all nodes expanded */ void allocate_ranks(graph_t* g) { int r,*cn; count_ranks(g,&cn); GD_rank(g) = N_NEW(GD_maxrank(g)+2,rank_t); for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { GD_rank(g)[r].an = GD_rank(g)[r].n = cn[r]; GD_rank(g)[r].av = GD_rank(g)[r].v = N_NEW(cn[r]+1,node_t*); } } /* install a node at the current right end of its rank */ void install_in_rank(graph_t* g, node_t* n) { int i,r; r = ND_rank(n); i = GD_rank(g)[r].n; if (GD_rank(g)[r].an <= 0) { agerr(AGERR, "install_in_rank %s %s rank %d i = %d an = 0\n", g->name,n->name,r,i); abort(); } GD_rank(g)[r].v[i] = n; ND_order(n) = i; GD_rank(g)[r].n++; assert(GD_rank(g)[r].n <= GD_rank(g)[r].an); #ifdef DEBUG { node_t *v; for (v = GD_nlist(g); v; v = ND_next(v)) if (v == n) break; assert(v != NULL); } #endif if (ND_order(n) > GD_rank(Root)[r].an) abort(); if ((r < GD_minrank(g)) || (r > GD_maxrank(g))) abort(); if (GD_rank(g)[r].v + ND_order(n) > GD_rank(g)[r].av + GD_rank(Root)[r].an) abort(); } /* install nodes in ranks. the initial ordering ensure that series-parallel * graphs such as trees are drawn with no crossings. it tries searching * in- and out-edges and takes the better of the two initial orderings. */ void build_ranks(graph_t* g, int pass) { int i,j; node_t *n,*n0; edge_t **otheredges; queue *q; q = new_queue(GD_n_nodes(g)); for (n = GD_nlist(g); n; n = ND_next(n)) MARK(n) = FALSE; #ifdef DEBUG { edge_t *e; for (n = GD_nlist(g); n; n = ND_next(n)) { for (i = 0; (e = ND_out(n).list[i]); i++) assert(MARK(e->head) == FALSE); for (i = 0; (e = ND_in(n).list[i]); i++) assert(MARK(e->tail) == FALSE); } } #endif for (i = GD_minrank(g); i <= GD_maxrank(g); i++) GD_rank(g)[i].n = 0; for (n = GD_nlist(g); n; n = ND_next(n)) { otheredges = ((pass == 0)? ND_in(n).list : ND_out(n).list); if (otheredges[0] != NULL) continue; if (MARK(n) == FALSE) { MARK(n) = TRUE; enqueue(q,n); while ((n0 = dequeue(q))) { if (ND_ranktype(n0) != CLUSTER) { install_in_rank(g,n0); enqueue_neighbors(q,n0,pass); } else { install_cluster(g,n0,pass,q); } } } } if (dequeue(q)) agerr(AGERR, "surprise\n"); for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { GD_rank(Root)[i].valid = FALSE; if (GD_left_to_right(g) && (GD_rank(g)[i].n > 0)) { int n,ndiv2; node_t **vlist = GD_rank(g)[i].v; n = GD_rank(g)[i].n - 1; ndiv2 = n / 2; for (j = 0; j <= ndiv2; j++) exchange(vlist[j],vlist[n - j]); } } if ((g == g->root) && ncross(g) > 0) transpose(g,FALSE); free_queue(q); } void enqueue_neighbors(queue* q, node_t* n0, int pass) { int i; edge_t *e; if (pass == 0) { for (i = 0; i < ND_out(n0).size ; i++) { e = ND_out(n0).list[i]; if ((MARK(e->head)) == FALSE) { MARK(e->head) = TRUE; enqueue(q,e->head); } } } else { for (i = 0; i < ND_in(n0).size ; i++) { e = ND_in(n0).list[i]; if ((MARK(e->tail)) == FALSE) { MARK(e->tail) = TRUE; enqueue(q,e->tail); } } } } /* construct nodes reachable from 'here' in post-order. * This is the same as doing a topological sort in reverse order. */ static int postorder(graph_t *g, node_t *v, node_t **list) { edge_t *e; int i,cnt = 0; MARK(v) = TRUE; if (ND_flat_out(v).size > 0) { for (i = 0; (e = ND_flat_out(v).list[i]); i++) { if ((ND_node_type(e->head) == NORMAL) & (NOT(agcontains(g,e->head)))) continue; if (ND_clust(e->head) != ND_clust(e->tail)) continue; if (MARK(e->head) == FALSE) cnt += postorder(g,e->head,list+cnt); } } list[cnt++] = v; return cnt; } static void flat_reorder(graph_t* g) { int i,j,r,pos,n_search,local_in_cnt, local_out_cnt; node_t *v,**left,**right,*t; node_t **temprank = NULL; edge_t *flat_e; if (GD_has_flat_edges(g) == FALSE) return; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { for (i = 0; i < GD_rank(g)[r].n; i++) MARK(GD_rank(g)[r].v[i]) = FALSE; temprank = ALLOC(i+1,temprank,node_t*); pos = 0; for (i = 0; i < GD_rank(g)[r].n; i++) { v = GD_rank(g)[r].v[i]; local_in_cnt = local_out_cnt = 0; for (j = 0; j < ND_flat_in(v).size; j++) { flat_e = ND_flat_in(v).list[j]; if (inside_cluster(g,flat_e->tail)) local_in_cnt++; } for (j = 0; j < ND_flat_out(v).size; j++) { flat_e = ND_flat_out(v).list[j]; if (inside_cluster(g,flat_e->head)) local_out_cnt++; } if ((local_in_cnt == 0) && (local_out_cnt == 0)) temprank[pos++] = v; else { if ((MARK(v) == FALSE) && (local_in_cnt == 0)) { left = temprank + pos; n_search = postorder(g,v,left); if (GD_left_to_right(g) == FALSE) { right = left + n_search - 1; while (left < right) { t = *left; *left = *right; *right = t; left++; right--; } } pos += n_search; } } } for (i = 0; i < GD_rank(g)[r].n; i++){ v = GD_rank(g)[r].v[i] = temprank[i]; ND_order(v) = i + (GD_rank(g)[r].v - GD_rank(Root)[r].v); } GD_rank(Root)[r].valid = FALSE; } if (temprank) free(temprank); } static void mincross_step(graph_t* g, int pass) { int r,other,first,last,dir; int hasfixed,reverse; if ((pass % 4) < 2) reverse = TRUE; else reverse = FALSE; if (pass % 2) { r = GD_maxrank(g) - 1; dir = -1; } /* up pass */ else { r = 1; dir = 1; } /* down pass */ if (pass % 2 == 0) { /* down pass */ first = GD_minrank(g) + 1; if (GD_minrank(g) > GD_minrank(Root)) first--; last = GD_maxrank(g); dir = 1; } else { /* up pass */ first = GD_maxrank(g) - 1; last = GD_minrank(g); if (GD_maxrank(g) < GD_maxrank(Root)) first++; dir = -1; } for (r = first; r != last + dir; r += dir) { other = r - dir; hasfixed = medians(g,r,other); reorder(g,r,reverse,hasfixed); } transpose(g,NOT(reverse)); } void transpose(graph_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; #ifdef NOTDEF /* don't run both the upward and downward passes- they cancel. i tried making it depend on whether an odd or even pass, but that didn't help. */ for (r = GD_maxrank(g); r >= GD_minrank(g); r--) if (GD_rank(g)[r].candidate) delta += transpose_step(g,r,reverse); #endif for (r = GD_minrank(g); r <= GD_maxrank(g); r++) if (GD_rank(g)[r].candidate) delta += transpose_step(g,r,reverse); /*} while (delta > ncross(g)*(1.0 - Convergence));*/ } while (delta >= 1); } int local_cross(elist l, int dir) { int i,j,is_out; int cross = 0; edge_t *e,*f; if (dir > 0) is_out = TRUE; else is_out = FALSE; for (i = 0; (e = l.list[i]); i++) { if (is_out) for (j = i+1; (f = l.list[j]); j++) { if ((ND_order(f->head) - ND_order(e->head)) * (ED_tail_port(f).p.x - ED_tail_port(e).p.x) < 0) cross += ED_xpenalty(e) * ED_xpenalty(f); } else for (j = i+1; (f = l.list[j]); j++) { if ((ND_order(f->tail) - ND_order(e->tail)) * (ED_head_port(f).p.x - ED_head_port(e).p.x) < 0) cross += ED_xpenalty(e) * ED_xpenalty(f); } } return cross; } int rcross(graph_t* g, int r) { static int *Count,C; int top,bot,cross,max,i,k; node_t **rtop,*v; cross = 0; max = 0; rtop = GD_rank(g)[r].v; if (C <= GD_rank(Root)[r+1].n) { C = GD_rank(Root)[r+1].n + 1; Count = ALLOC(C,Count,int); } for (i = 0; i < GD_rank(g)[r+1].n; i++) Count[i] = 0; for (top = 0; top < GD_rank(g)[r].n; top++) { register edge_t *e; if (max > 0) { for (i = 0; (e = rtop[top]->u.out.list[i]); i++) { for (k = ND_order(e->head) + 1; k <= max; k++) cross += Count[k] * ED_xpenalty(e); } } for (i = 0; (e = rtop[top]->u.out.list[i]); i++) { register int inv = ND_order(e->head); if (inv > max) max = inv; Count[inv] += ED_xpenalty(e); } } for (top = 0; top < GD_rank(g)[r].n; top++) { v = GD_rank(g)[r].v[top]; if (ND_has_port(v)) cross += local_cross(ND_out(v),1); } for (bot = 0; bot < GD_rank(g)[r+1].n; bot++) { v = GD_rank(g)[r+1].v[bot]; if (ND_has_port(v)) cross += local_cross(ND_in(v),-1); } return cross; } int ncross(graph_t* g) { int r,count,nc; g = Root; count = 0; for (r = GD_minrank(g); r < GD_maxrank(g); r++) { if (GD_rank(g)[r].valid) count += GD_rank(g)[r].cache_nc; else { nc = GD_rank(g)[r].cache_nc = rcross(g,r); count += nc; GD_rank(g)[r].valid = TRUE; } } return count; } int ordercmpf(int *i0, int *i1) { return (*i0) - (*i1); } int out_cross(node_t *v, node_t *w) { register edge_t **e1,**e2; register int inv, cross = 0,t; for (e2 = ND_out(w).list; *e2; e2++) { register int cnt = (*e2)->u.xpenalty; inv = ((*e2)->head)->u.order; for (e1 = ND_out(v).list; *e1; e1++) { t = ((*e1)->head)->u.order - inv; if ((t > 0) || ((t == 0) && ((*e1)->u.head_port.p.x > (*e2)->u.head_port.p.x))) cross += (*e1)->u.xpenalty * cnt; } } return cross; } int in_cross(node_t *v,node_t *w) { register edge_t **e1,**e2; register int inv, cross = 0, t; for (e2 = ND_in(w).list; *e2; e2++) { register int cnt = (*e2)->u.xpenalty; inv = ((*e2)->tail)->u.order; for (e1 = ND_in(v).list; *e1; e1++) { t = ((*e1)->tail)->u.order - inv; if ((t > 0) || ((t == 0) && ((*e1)->u.tail_port.p.x > (*e2)->u.tail_port.p.x))) cross += (*e1)->u.xpenalty * cnt; } } return cross; } #define VAL(node,port) (MC_SCALE * (node)->u.order + (port).order) static boolean medians(graph_t *g, int r0, int r1) { int i,j,j0,lm,rm,lspan,rspan,*list; node_t *n,**v; edge_t *e; boolean hasfixed = FALSE; list = TI_list; v = GD_rank(g)[r0].v; for (i = 0; i < GD_rank(g)[r0].n; i++) { n = v[i]; j = 0; if (r1 > r0) for (j0 = 0; (e = ND_out(n).list[j0]); j0++) { if (ED_xpenalty(e) > 0) list[j++] = VAL(e->head,ED_head_port(e)); } else for (j0 = 0; (e = ND_in(n).list[j0]); j0++) { if (ED_xpenalty(e) > 0) list[j++] = VAL(e->tail,ED_tail_port(e)); } switch(j) { case 0: ND_mval(n) = -1; break; case 1: ND_mval(n) = list[0]; break; case 2: ND_mval(n) = (list[0] + list[1])/2; break; default: qsort(list,j,sizeof(int),(qsort_cmpf)ordercmpf); if (j % 2) ND_mval(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_mval(n) = (list[lm] + list[rm])/2; else { int w = list[lm]*rspan + list[rm]*lspan; ND_mval(n) = w / (lspan + rspan); } } } } 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_mval(n); } return hasfixed; } int transpose_step(graph_t* g, int r, int reverse) { int i,c0,c1,rv; node_t *v,*w; rv = 0; GD_rank(g)[r].candidate = FALSE; for (i = 0; i < GD_rank(g)[r].n - 1; 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 > 0) { c0 += in_cross(v,w); c1 += in_cross(w,v); } if (GD_rank(g)[r+1].n > 0) { c0 += out_cross(v,w); c1 += out_cross(w,v); } if ((c1 < c0) || ((c0 > 0) && reverse && (c1 == c0))) { exchange(v,w); rv += (c0 - c1); GD_rank(Root)[r].valid = FALSE; GD_rank(g)[r].candidate = TRUE; if (r > GD_minrank(g)) { GD_rank(Root)[r-1].valid = FALSE; GD_rank(g)[r-1].candidate = TRUE; } if (r < GD_maxrank(g)) { GD_rank(Root)[r+1].valid = FALSE; GD_rank(g)[r+1].candidate = TRUE; } } } return rv; } void exchange(node_t *v, node_t *w) { int vi,wi,r; r = ND_rank(v); vi = ND_order(v); wi = ND_order(w); ND_order(v) = wi; GD_rank(Root)[r].v[wi] = v; ND_order(w) = vi; GD_rank(Root)[r].v[vi] = w; } void reorder(graph_t *g, int r, int reverse, int hasfixed) { int changed = 0, nelt; boolean muststay,sawclust; node_t **vlist = GD_rank(g)[r].v; node_t **lp,**rp,**ep = vlist + GD_rank(g)[r].n; for (nelt = GD_rank(g)[r].n - 1; nelt >= 0; nelt--) { lp = vlist; while (lp < ep) { /* find leftmost node that can be compared */ while ((lp < ep) && ((*lp)->u.mval < 0)) lp++; if (lp >= ep) break; /* find the node that can be compared */ sawclust = muststay = FALSE; for (rp = lp + 1; rp < ep; rp++) { if (sawclust && (*rp)->u.clust) continue; /* ### */ if (left2right(g,*lp,*rp)) { muststay = TRUE; break; } if ((*rp)->u.mval >= 0) break; if ((*rp)->u.clust) sawclust = TRUE; /* ### */ } if (rp >= ep) break; if (muststay == FALSE) { register int p1 = ((*lp)->u.mval); register int p2 = ((*rp)->u.mval); if ((p1 > p2) || ((p1 == p2) && (reverse))) { exchange(*lp,*rp); changed++; } } lp = rp; } if ((hasfixed == FALSE) && (reverse == FALSE)) ep--; } if (changed) { GD_rank(Root)[r].valid = FALSE; if (r > 0) GD_rank(Root)[r-1].valid = FALSE; } } static int nodeposcmpf(node_t **n0, node_t **n1) { return ((*n0)->u.order - (*n1)->u.order); } static int edgeidcmpf(edge_t **e0, edge_t **e1) { return ((*e0)->id - (*e1)->id); } /* following code deals with weights of edges of "virtual" nodes */ #define ORDINARY 0 #define SINGLETON 1 #define VIRTUALNODE 2 #define NTYPES 3 #define C_EE 1 #define C_VS 2 #define C_SS 2 #define C_VV 4 static int table[NTYPES][NTYPES] = { /* ordinary */ {C_EE, C_EE, C_EE}, /* singleton */ {C_EE, C_SS, C_VS}, /* virtual */ {C_EE, C_VS, C_VV} }; static int endpoint_class(node_t* n) { if (ND_node_type(n) == VIRTUAL) return VIRTUALNODE; if (ND_weight_class(n) <= 1) return SINGLETON; return ORDINARY; } void virtual_weight(edge_t* e) { int t; t = table[endpoint_class(e->tail)][endpoint_class(e->head)]; ED_weight(e) *= t; } void ordered_edges(graph_t* g) { char *ordering; if ((ordering = agget(g,"ordering"))) { if (streq(ordering,"out")) do_ordering(g,TRUE); else if (streq(ordering,"in")) do_ordering(g,FALSE); else if (ordering[0]) agerr(AGERR, "ordering '%s' not recognized.\n",ordering); } else { /* search meta-graph to find subgraphs that may be ordered */ graph_t *mg,*subg; node_t *mm,*mn; edge_t *me; mm = g->meta_node; mg = mm->graph; for (me = agfstout(mg,mm); me; me = agnxtout(mg,me)) { mn = me->head; subg = agusergraph(mn); /* clusters are processed by seperate calls to ordered_edges */ if (!is_cluster(subg)) ordered_edges(subg); } } } static int betweenclust(edge_t *e) { while (ED_to_orig(e)) e = ED_to_orig(e); return (ND_clust(e->tail) != ND_clust(e->head)); } void do_ordering(graph_t* g, int outflag) { int i, ne; node_t *n, *u, *v; edge_t *e, *f, *fe; edge_t **sortlist = TE_list; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (ND_clust(n)) continue; if (outflag) { for (i = ne = 0; (e = ND_out(n).list[i]); i++) if (!betweenclust(e)) sortlist[ne++] = e; } else { for (i = ne = 0; (e = ND_in(n).list[i]); i++) if (!betweenclust(e)) sortlist[ne++] = e; } if (ne <= 1) continue; sortlist[ne] = 0; qsort(sortlist,ne,sizeof(sortlist[0]),(qsort_cmpf)edgeidcmpf); for (ne = 1; (f = sortlist[ne]); ne++) { e = sortlist[ne - 1]; if (outflag) {u = e->head; v = f->head;} else {u = e->tail; v = f->tail;} if (find_flat_edge(u, v)) continue; fe = new_virtual_edge(u,v,NULL); ED_edge_type(fe) = FLATORDER; flat_edge(g,fe); } } } /* merges the connected components of g */ void merge_components(graph_t* g) { int c; node_t *u,*v; if (GD_comp(g).size <= 1) return; u = NULL; for (c = 0; c < GD_comp(g).size; c++) { v = GD_comp(g).list[c]; if (u) ND_next(u) = v; ND_prev(v) = u; while (ND_next(v)) { v = ND_next(v); } u = v; } GD_comp(g).size = 1; GD_nlist(g) = GD_comp(g).list[0]; GD_minrank(g) = GlobalMinRank; GD_maxrank(g) = GlobalMaxRank; } #ifdef DEBUG void check_rs(graph_t* g, int null_ok) { int i,r; node_t *v,*prev; fprintf(stderr,"\n\n%s:\n",g->name); for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { fprintf(stderr,"%d: ",r); prev = NULL; for (i = 0; i < GD_rank(g)[r].n; i++) { v = GD_rank(g)[r].v[i]; if (v == NULL) { fprintf(stderr,"NULL\t"); if(null_ok==FALSE)abort(); } else { fprintf(stderr,"%s\t",v->name); assert(ND_rank(v) == r); assert(v != prev); prev = v; } } fprintf(stderr,"\n"); } } void check_order(void) { int i,r; node_t *v; graph_t *g = Root; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { assert(GD_rank(g)[r].v[GD_rank(g)[r].n] == NULL); for (i = 0; v = GD_rank(g)[r].v[i]; i++) assert (ND_order(v) == i); } } #endif static void mincross_options(graph_t* g) { char *p; double f; /* set default values */ MinQuit = 8; MaxIter = 24; Convergence = .995; p = agget(g,"mclimit"); if (p && ((f = atof(p)) > 0.0)) { MinQuit = MAX(1,MinQuit * f); MaxIter = MAX(1,MaxIter * f); } } int flat_mval(node_t* n) { int i; edge_t *e,**fl; node_t *nn; if ((ND_in(n).size == 0) && (ND_out(n).size == 0)) { if (ND_flat_in(n).size > 0) { fl = ND_flat_in(n).list; nn = fl[0]->tail; for (i = 1; (e = fl[i]); i++) if (ND_order(e->tail) > ND_order(nn)) nn = e->tail; ND_mval(n) = ND_mval(nn) + 1; return FALSE; } else if (ND_flat_out(n).size > 0) { fl = ND_flat_out(n).list; nn = fl[0]->head; for (i = 1; (e = fl[i]); i++) if (ND_order(e->head) < ND_order(nn)) nn = e->head; ND_mval(n) = ND_mval(nn) - 1; return FALSE; } } return TRUE; } #ifdef DEBUG void check_exchange(node_t *v, node_t *w) { int i,r; node_t *u; if ((ND_clust(v) == NULL) && (ND_clust(w) == NULL)) return; assert ((ND_clust(v) == NULL) || (ND_clust(w) == NULL)); assert(ND_rank(v) == ND_rank(w)); assert(ND_order(v) < ND_order(w)); r = ND_rank(v); for (i = ND_order(v) + 1; i < ND_order(w); i++) { u = GD_rank(v->graph)[r].v[i]; if (ND_clust(u)) abort(); } } void check_vlists(graph_t* g) { int c,i,j,r; node_t *u; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { for (i = 0; i < GD_rank(g)[r].n; i++) { u = GD_rank(g)[r].v[i]; j = ND_order(u); assert (GD_rank(Root)[r].v[j] == u); } if (GD_rankleader(g)) { u = GD_rankleader(g)[r]; j = ND_order(u); assert (GD_rank(Root)[r].v[j] == u); } } for (c = 1; c <= GD_n_cluster(g); c++) check_vlists(GD_clust(g)[c]); } void node_in_root_vlist(node_t* n) { node_t **vptr; for (vptr = GD_rank(Root)[ND_rank(n)].v; *vptr; vptr++) if (*vptr == n) break; if (*vptr == 0) abort(); } #endif /* DEBUG code */