Graphviz  2.29.20120524.0446
lib/dotgen/rank.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 /*
00016  * Rank the nodes of a directed graph, subject to user-defined
00017  * sets of nodes to be kept on the same, min, or max rank.
00018  * The temporary acyclic fast graph is constructed and ranked
00019  * by a network-simplex technique.  Then ranks are propagated
00020  * to non-leader nodes and temporary edges are deleted.
00021  * Leaf nodes and top-level clusters are left collapsed, though.
00022  * Assigns global minrank and maxrank of graph and all clusters.
00023  *
00024  * TODO: safety code.  must not be in two clusters at same level.
00025  *  must not be in same/min/max/rank and a cluster at the same time.
00026  *  watch out for interactions between leaves and clusters.
00027  */
00028 
00029 #include        "dot.h"
00030 
00031 static void dot1_rank(graph_t * g, aspect_t* asp);
00032 #ifdef WITH_CGRAPH
00033 static void dot2_rank(graph_t * g, aspect_t* asp);
00034 #endif
00035 
00036 static void 
00037 renewlist(elist * L)
00038 {
00039     int i;
00040     for (i = L->size; i >= 0; i--)
00041         L->list[i] = NULL;
00042     L->size = 0;
00043 }
00044 
00045 static void 
00046 cleanup1(graph_t * g)
00047 {
00048     node_t *n;
00049     edge_t *e, *f;
00050     int c;
00051 
00052     for (c = 0; c < GD_comp(g).size; c++) {
00053         GD_nlist(g) = GD_comp(g).list[c];
00054         for (n = GD_nlist(g); n; n = ND_next(n)) {
00055             renewlist(&ND_in(n));
00056             renewlist(&ND_out(n));
00057             ND_mark(n) = FALSE;
00058         }
00059     }
00060     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00061         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00062             f = ED_to_virt(e);
00063             /* Null out any other references to f to make sure we don't 
00064              * handle it a second time. For example, parallel multiedges 
00065              * share a virtual edge.
00066              */
00067             if (f && (e == ED_to_orig(f))) {
00068                 edge_t *e1, *f1;
00069 #ifndef WITH_CGRAPH
00070                 for (e1 = agfstout(g, n); e1; e1 = agnxtout(g, e1)) {
00071                     if (e != e1) {
00072                         f1 = ED_to_virt(e1);
00073                         if (f1 && (f == f1)) {
00074                             ED_to_virt(e1) = NULL;
00075                         }
00076                     }
00077                 }
00078 #else
00079                 node_t *n1;
00080                 for (n1 = agfstnode(g); n1; n1 = agnxtnode(g, n1)) {
00081                     for (e1 = agfstout(g, n1); e1; e1 = agnxtout(g, e1)) {
00082                         if (e != e1) {
00083                             f1 = ED_to_virt(e1);
00084                             if (f1 && (f == f1)) {
00085                                 ED_to_virt(e1) = NULL;
00086                             }
00087                         }
00088                     }
00089                 }
00090                 free(f->base.data);
00091 #endif
00092                 free(f);
00093             }
00094             ED_to_virt(e) = NULL;
00095         }
00096     }
00097     free(GD_comp(g).list);
00098     GD_comp(g).list = NULL;
00099     GD_comp(g).size = 0;
00100 }
00101 
00102 /* When there are edge labels, extra ranks are reserved here for the virtual
00103  * nodes of the labels.  This is done by doubling the input edge lengths.
00104  * The input rank separation is adjusted to compensate.
00105  */
00106 static void 
00107 edgelabel_ranks(graph_t * g)
00108 {
00109     node_t *n;
00110     edge_t *e;
00111 
00112     if (GD_has_labels(g) & EDGE_LABEL) {
00113         for (n = agfstnode(g); n; n = agnxtnode(g, n))
00114             for (e = agfstout(g, n); e; e = agnxtout(g, e))
00115                 ED_minlen(e) *= 2;
00116         GD_ranksep(g) = (GD_ranksep(g) + 1) / 2;
00117     }
00118 }
00119 
00120 /* Merge the nodes of a min, max, or same rank set. */
00121 static void 
00122 collapse_rankset(graph_t * g, graph_t * subg, int kind)
00123 {
00124     node_t *u, *v;
00125 
00126     u = v = agfstnode(subg);
00127     if (u) {
00128         ND_ranktype(u) = kind;
00129         while ((v = agnxtnode(subg, v))) {
00130             UF_union(u, v);
00131             ND_ranktype(v) = ND_ranktype(u);
00132         }
00133         switch (kind) {
00134         case MINRANK:
00135         case SOURCERANK:
00136             if (GD_minset(g) == NULL)
00137                 GD_minset(g) = u;
00138             else
00139                 GD_minset(g) = UF_union(GD_minset(g), u);
00140             break;
00141         case MAXRANK:
00142         case SINKRANK:
00143             if (GD_maxset(g) == NULL)
00144                 GD_maxset(g) = u;
00145             else
00146                 GD_maxset(g) = UF_union(GD_maxset(g), u);
00147             break;
00148         }
00149         switch (kind) {
00150         case SOURCERANK:
00151             ND_ranktype(GD_minset(g)) = kind;
00152             break;
00153         case SINKRANK:
00154             ND_ranktype(GD_maxset(g)) = kind;
00155             break;
00156         }
00157     }
00158 }
00159 
00160 static int 
00161 rank_set_class(graph_t * g)
00162 {
00163     static char *name[] = { "same", "min", "source", "max", "sink", NULL };
00164     static int class[] =
00165         { SAMERANK, MINRANK, SOURCERANK, MAXRANK, SINKRANK, 0 };
00166     int val;
00167 
00168     if (is_cluster(g))
00169         return CLUSTER;
00170     val = maptoken(agget(g, "rank"), name, class);
00171     GD_set_type(g) = val;
00172     return val;
00173 }
00174 
00175 static int 
00176 make_new_cluster(graph_t * g, graph_t * subg)
00177 {
00178     int cno;
00179     cno = ++(GD_n_cluster(g));
00180     GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g));
00181     GD_clust(g)[cno] = subg;
00182     do_graph_label(subg);
00183     return cno;
00184 }
00185 
00186 static void 
00187 node_induce(graph_t * par, graph_t * g)
00188 {
00189     node_t *n, *nn;
00190     edge_t *e;
00191     int i;
00192 
00193     /* enforce that a node is in at most one cluster at this level */
00194     for (n = agfstnode(g); n; n = nn) {
00195         nn = agnxtnode(g, n);
00196         if (ND_ranktype(n)) {
00197             agdelete(g, n);
00198             continue;
00199         }
00200         for (i = 1; i < GD_n_cluster(par); i++)
00201             if (agcontains(GD_clust(par)[i], n))
00202                 break;
00203         if (i < GD_n_cluster(par))
00204             agdelete(g, n);
00205         ND_clust(n) = NULL;
00206     }
00207 
00208     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00209         for (e = agfstout(agroot(g), n); e; e = agnxtout(agroot(g), e)) {
00210             if (agcontains(g, aghead(e)))
00211 #ifndef WITH_CGRAPH
00212                 aginsert(g, e);
00213 #else /* WITH_CGRAPH */
00214                 agsubedge(g,e,1);
00215 #endif /* WITH_CGRAPH */
00216         }
00217     }
00218 }
00219 
00220 void 
00221 dot_scan_ranks(graph_t * g)
00222 {
00223     node_t *n, *leader = NULL;
00224     GD_minrank(g) = MAXSHORT;
00225     GD_maxrank(g) = -1;
00226     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00227         if (GD_maxrank(g) < ND_rank(n))
00228             GD_maxrank(g) = ND_rank(n);
00229         if (GD_minrank(g) > ND_rank(n))
00230             GD_minrank(g) = ND_rank(n);
00231         if (leader == NULL)
00232             leader = n;
00233         else {
00234             if (ND_rank(n) < ND_rank(leader))
00235                 leader = n;
00236         }
00237     }
00238     GD_leader(g) = leader;
00239 }
00240 
00241 static void
00242 cluster_leader(graph_t * clust)
00243 {
00244     node_t *leader, *n;
00245     int maxrank = 0;
00246 
00247     /* find number of ranks and select a leader */
00248     leader = NULL;
00249     for (n = GD_nlist(clust); n; n = ND_next(n)) {
00250         if ((ND_rank(n) == 0) && (ND_node_type(n) == NORMAL))
00251             leader = n;
00252         if (maxrank < ND_rank(n))
00253             maxrank = ND_rank(n);
00254     }
00255     assert(leader != NULL);
00256     GD_leader(clust) = leader;
00257 
00258     for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) {
00259         assert((ND_UF_size(n) <= 1) || (n == leader));
00260         UF_union(n, leader);
00261         ND_ranktype(n) = CLUSTER;
00262     }
00263 }
00264 
00265 /*
00266  * A cluster is collapsed in three steps.
00267  * 1) The nodes of the cluster are ranked locally.
00268  * 2) The cluster is collapsed into one node on the least rank.
00269  * 3) In class1(), any inter-cluster edges are converted using
00270  *    the "virtual node + 2 edges" trick.
00271  */
00272 static void 
00273 collapse_cluster(graph_t * g, graph_t * subg)
00274 {
00275     if (GD_parent(subg)) {
00276 #ifndef WITH_CGRAPH
00277         agerr(AGWARN, "Cluster %s is multiply defined in %s and %s - this may cause problems.\n", subg->name,
00278                 g->name, GD_parent(subg)->name);
00279 #endif
00280         return;
00281     }
00282     GD_parent(subg) = g;
00283     node_induce(g, subg);
00284     if (agfstnode(subg) == NULL)
00285         return;
00286     make_new_cluster(g, subg);
00287     if (CL_type == LOCAL) {
00288         dot1_rank(subg, 0);
00289         cluster_leader(subg);
00290     } else
00291         dot_scan_ranks(subg);
00292 }
00293 
00294 /* Execute union commands for "same rank" subgraphs and clusters. */
00295 static void 
00296 collapse_sets(graph_t *rg, graph_t *g)
00297 {
00298     int c;
00299     graph_t  *subg;
00300 #ifdef OBSOLETE
00301     node_t *n;
00302 #endif
00303 
00304 #ifndef WITH_CGRAPH
00305     graph_t *mg;
00306     node_t *mn;
00307     edge_t *me;
00308     mg = g->meta_node->graph;
00309     for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
00310         mn = aghead(me);
00311         subg = agusergraph(mn);
00312 #else /* WITH_CGRAPH */
00313     for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
00314 #endif /* WITH_CGRAPH */
00315         c = rank_set_class(subg);
00316         if (c) {
00317             if ((c == CLUSTER) && CL_type == LOCAL)
00318                 collapse_cluster(rg, subg);
00319             else
00320                 collapse_rankset(rg, subg, c);
00321         }
00322         else collapse_sets(rg, subg);
00323 
00324 #ifdef OBSOLETE
00325  Collapsing leaves is currently obsolete
00326 
00327         /* mark nodes with ordered edges so their leaves are not collapsed */
00328         if (agget(subg, "ordering"))
00329             for (n = agfstnode(subg); n; n = agnxtnode(subg, n))
00330                 ND_order(n) = 1;
00331 #endif
00332     }
00333 }
00334 
00335 static void 
00336 find_clusters(graph_t * g)
00337 {
00338     graph_t *subg;
00339 #ifndef WITH_CGRAPH
00340     graph_t *mg;
00341     node_t *mn;
00342     edge_t *me;
00343 
00344     mg = g->meta_node->graph;
00345     for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
00346         mn = me->head;
00347         subg = agusergraph(mn);
00348 #else /* WITH_CGRAPH */
00349     for (subg = agfstsubg(agroot(g)); subg; subg = agnxtsubg(subg)) {
00350 #endif /* WITH_CGRAPH */
00351         if (GD_set_type(subg) == CLUSTER)
00352             collapse_cluster(g, subg);
00353     }
00354 }
00355 
00356 static void 
00357 set_minmax(graph_t * g)
00358 {
00359     int c;
00360 
00361     GD_minrank(g) += ND_rank(GD_leader(g));
00362     GD_maxrank(g) += ND_rank(GD_leader(g));
00363     for (c = 1; c <= GD_n_cluster(g); c++)
00364         set_minmax(GD_clust(g)[c]);
00365 }
00366 
00367 /* To ensure that min and max rank nodes always have the intended rank
00368  * assignment, reverse any incompatible edges.
00369  */
00370 static point 
00371 minmax_edges(graph_t * g)
00372 {
00373     node_t *n;
00374     edge_t *e;
00375     point  slen;
00376 
00377     slen.x = slen.y = 0;
00378     if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL))
00379         return slen;
00380     if (GD_minset(g) != NULL)
00381         GD_minset(g) = UF_find(GD_minset(g));
00382     if (GD_maxset(g) != NULL)
00383         GD_maxset(g) = UF_find(GD_maxset(g));
00384 
00385     if ((n = GD_maxset(g))) {
00386         slen.y = (ND_ranktype(GD_maxset(g)) == SINKRANK);
00387         while ((e = ND_out(n).list[0])) {
00388             assert(aghead(e) == UF_find(aghead(e)));
00389             reverse_edge(e);
00390         }
00391     }
00392     if ((n = GD_minset(g))) {
00393         slen.x = (ND_ranktype(GD_minset(g)) == SOURCERANK);
00394         while ((e = ND_in(n).list[0])) {
00395             assert(agtail(e) == UF_find(agtail(e)));
00396             reverse_edge(e);
00397         }
00398     }
00399     return slen;
00400 }
00401     
00402 static int 
00403 minmax_edges2(graph_t * g, point slen)
00404 {
00405     node_t *n;
00406     edge_t *e = 0;
00407 
00408     if ((GD_maxset(g)) || (GD_minset(g))) {
00409         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00410             if (n != UF_find(n))
00411                 continue;
00412             if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) {
00413                 e = virtual_edge(n, GD_maxset(g), NULL);
00414                 ED_minlen(e) = slen.y;
00415                 ED_weight(e) = 0;
00416             }
00417             if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) {
00418                 e = virtual_edge(GD_minset(g), n, NULL);
00419                 ED_minlen(e) = slen.x;
00420                 ED_weight(e) = 0;
00421             }
00422         }
00423     }
00424     return (e != 0);
00425 }
00426 
00427 /* Run the network simplex algorithm on each component. */
00428 void rank1(graph_t * g)
00429 {
00430     int maxiter = INT_MAX;
00431     int c;
00432     char *s;
00433 
00434     if ((s = agget(g, "nslimit1")))
00435         maxiter = atof(s) * agnnodes(g);
00436     for (c = 0; c < GD_comp(g).size; c++) {
00437         GD_nlist(g) = GD_comp(g).list[c];
00438         rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter);       /* TB balance */
00439     }
00440 }
00441 
00442 /* 
00443  * Assigns ranks of non-leader nodes.
00444  * Expands same, min, max rank sets.
00445  * Leaf sets and clusters remain merged.
00446  * Sets minrank and maxrank appropriately.
00447  */
00448 static void expand_ranksets(graph_t * g, aspect_t* asp)
00449 {
00450     int c;
00451     node_t *n, *leader;
00452 
00453     if ((n = agfstnode(g))) {
00454         GD_minrank(g) = MAXSHORT;
00455         GD_maxrank(g) = -1;
00456         while (n) {
00457             leader = UF_find(n);
00458             /* The following works because ND_rank(n) == 0 if n is not in a
00459              * cluster, and ND_rank(n) = the local rank offset if n is in
00460              * a cluster. */
00461             if ((leader != n) && (!asp || (ND_rank(n) == 0)))
00462                 ND_rank(n) += ND_rank(leader);
00463 
00464             if (GD_maxrank(g) < ND_rank(n))
00465                 GD_maxrank(g) = ND_rank(n);
00466             if (GD_minrank(g) > ND_rank(n))
00467                 GD_minrank(g) = ND_rank(n);
00468 
00469             if (ND_ranktype(n) && (ND_ranktype(n) != LEAFSET))
00470                 UF_singleton(n);
00471             n = agnxtnode(g, n);
00472         }
00473         if (g == agroot(g)) {
00474             if (CL_type == LOCAL) {
00475                 for (c = 1; c <= GD_n_cluster(g); c++)
00476                     set_minmax(GD_clust(g)[c]);
00477             } else {
00478                 find_clusters(g);
00479             }
00480         }
00481     } else {
00482         GD_minrank(g) = GD_maxrank(g) = 0;
00483     }
00484 }
00485 
00486 #ifdef ALLOW_LEVELS
00487 void
00488 setRanks (graph_t* g, attrsym_t* lsym)
00489 {
00490     node_t* n;
00491     char*   s;
00492     char*   ep;
00493     long    v;
00494 
00495     for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
00496         s = agxget (n, lsym->index);
00497         v = strtol (s, &ep, 10);
00498         if (ep == s)
00499             agerr(AGWARN, "no level attribute for node \"%s\"\n", n->name);
00500         ND_rank(n) = v;
00501     }
00502 }
00503 #endif
00504 
00505 #ifdef UNUSED
00506 static node_t **virtualEdgeHeadList = NULL;
00507 static node_t **virtualEdgeTailList = NULL;
00508 static int nVirtualEdges = 0;
00509 
00510 static void
00511 saveVirtualEdges(graph_t *g)
00512 {
00513     edge_t *e;
00514     node_t *n;
00515     int cnt = 0;
00516     int lc;
00517     
00518     if (virtualEdgeHeadList != NULL) {
00519         free(virtualEdgeHeadList);
00520     }
00521     if (virtualEdgeTailList != NULL) {
00522         free(virtualEdgeTailList);
00523     }
00524 
00525   /* allocate memory */
00526     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00527         for (lc = 0; lc < ND_in(n).size; lc++) {
00528             e = ND_in(n).list[lc];
00529             if (ED_edge_type(e) == VIRTUAL) cnt++;
00530         }
00531     }
00532 
00533     nVirtualEdges = cnt;
00534     virtualEdgeHeadList = N_GNEW(cnt, node_t*);
00535     virtualEdgeTailList = N_GNEW(cnt, node_t*);
00536 
00537     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00538         for (lc = 0, cnt = 0; lc < ND_in(n).size; lc++) {
00539             e = ND_in(n).list[lc];
00540             if (ED_edge_type(e) == VIRTUAL) {
00541                 virtualEdgeHeadList[cnt] = e->head;
00542                 virtualEdgeTailList[cnt] = e->tail;
00543                 if (Verbose)
00544                     printf("saved virtual edge: %s->%s\n", 
00545                         virtualEdgeTailList[cnt]->name, 
00546                         virtualEdgeHeadList[cnt]->name);            
00547                 cnt++;
00548             }
00549         }
00550     }
00551 }
00552 
00553 static void
00554 restoreVirtualEdges(graph_t *g)
00555 {
00556     int i;
00557     edge_t e;
00558 
00559     for (i = 0; i < nVirtualEdges; i++) {
00560         if (virtualEdgeTailList[i] && virtualEdgeHeadList[i]) {
00561             if (Verbose)
00562                 printf("restoring virtual edge: %s->%s\n", 
00563                     virtualEdgeTailList[i]->name, virtualEdgeHeadList[i]->name);
00564             virtual_edge(virtualEdgeTailList[i], virtualEdgeHeadList[i], NULL);
00565         }
00566     }
00567     if (Verbose)
00568         printf("restored %d virt edges\n", nVirtualEdges);
00569 }
00570 #endif
00571 
00572 /* dot1_rank:
00573  * asp != NULL => g is root
00574  */
00575 static void dot1_rank(graph_t * g, aspect_t* asp)
00576 {
00577     point p;
00578 #ifdef ALLOW_LEVELS
00579     attrsym_t* N_level;
00580 #endif
00581     edgelabel_ranks(g);
00582 
00583     if (asp) {
00584         init_UF_size(g);
00585         initEdgeTypes(g);
00586     }
00587 
00588     collapse_sets(g,g);
00589     /*collapse_leaves(g); */
00590     class1(g);
00591     p = minmax_edges(g);
00592     decompose(g, 0);
00593     if (asp && ((GD_comp(g).size > 1)||(GD_n_cluster(g) > 0))) {
00594         asp->badGraph = 1;
00595         asp = NULL;
00596     }
00597     acyclic(g);
00598     if (minmax_edges2(g, p))
00599         decompose(g, 0);
00600 #ifdef ALLOW_LEVELS
00601     if ((N_level = agfindattr(g->proto->n, "level")))
00602         setRanks(g, N_level);
00603     else
00604 #endif
00605 
00606     if (asp)
00607         rank3(g, asp);
00608     else
00609         rank1(g);
00610 
00611     expand_ranksets(g, asp);
00612     cleanup1(g);
00613 }
00614 
00615 void dot_rank(graph_t * g, aspect_t* asp)
00616 {
00617 #ifdef WITH_CGRAPH
00618     if (agget (g, "newrank")) {
00619         GD_flags(g) |= NEW_RANK;
00620         dot2_rank (g, asp);
00621     }
00622     else
00623 #endif
00624         dot1_rank (g, asp);
00625 }
00626 
00627 int is_cluster(graph_t * g)
00628 {
00629     return (strncmp(agnameof(g), "cluster", 7) == 0);
00630 }
00631 
00632 #ifdef OBSOLETE
00633 static node_t*
00634 merge_leaves(graph_t * g, node_t * cur, node_t * new)
00635 {
00636     node_t *rv;
00637 
00638     if (cur == NULL)
00639         rv = new;
00640     else {
00641         rv = UF_union(cur, new);
00642         ND_ht(rv) = MAX(ND_ht(cur), ND_ht(new));
00643         ND_lw(rv) = ND_lw(cur) + ND_lw(new) + GD_nodesep(g) / 2;
00644         ND_rw(rv) = ND_rw(cur) + ND_rw(new) + GD_nodesep(g) / 2;
00645     }
00646     return rv;
00647 }
00648 
00649 static void 
00650 potential_leaf(graph_t * g, edge_t * e, node_t * leaf)
00651 {
00652     node_t *par;
00653 
00654     if ((ED_tail_port(e).p.x) || (ED_head_port(e).p.x))
00655         return;
00656     if ((ED_minlen(e) != 1) || (ND_order(agtail(e)) > 0))
00657         return;
00658     par = ((leaf != aghead(e)) ? aghead(e) : agtail(e));
00659     ND_ranktype(leaf) = LEAFSET;
00660     if (par == agtail(e))
00661         GD_outleaf(par) = merge_leaves(g, GD_outleaf(par), leaf);
00662     else
00663         GD_inleaf(par) = merge_leaves(g, GD_inleaf(par), leaf);
00664 }
00665 
00666 static void 
00667 collapse_leaves(graph_t * g)
00668 {
00669     node_t *n;
00670     edge_t *e;
00671 
00672     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00673 
00674         /* consider n as a potential leaf of some other node. */
00675         if ((ND_ranktype(n) != NOCMD) || (ND_order(n)))
00676             continue;
00677         if (agfstout(g, n) == NULL) {
00678             if ((e = agfstin(g, n)) && (agnxtin(g, e) == NULL)) {
00679 #ifndef WITH_CGRAPH
00680                 potential_leaf(g, e, n);
00681 #else
00682                 potential_leaf(g, AGMKOUT(e), n);
00683 #endif
00684                 continue;
00685             }
00686         }
00687         if (agfstin(g, n) == NULL) {
00688             if ((e = agfstout(g, n)) && (agnxtout(g, e) == NULL)) {
00689                 potential_leaf(g, e, n);
00690                 continue;
00691             }
00692         }
00693     }
00694 }
00695 #endif
00696 
00697 #ifdef WITH_CGRAPH
00698 /* new ranking code:
00699  * Allows more constraints
00700  * Copy of level.c in dotgen2
00701  * Many of the utility functions are simpler or gone with
00702  * cgraph library.
00703  */
00704 #define BACKWARD_PENALTY        1000
00705 #define STRONG_CLUSTER_WEIGHT   1000
00706 #define NORANK          6
00707 #define ROOT    "\177root"
00708 #define TOPNODE     "\177top"
00709 #define BOTNODE     "\177bot"
00710 
00711 /* hops is not used in dot, so we overload it to 
00712  * contain the index of the connected component
00713  */
00714 #define ND_comp(n)  ND_hops(n)   
00715 
00716 extern int rank2(Agraph_t *, int, int, int);
00717 
00718 static void set_parent(graph_t* g, graph_t* p) 
00719 {
00720     GD_parent(g) = p;
00721     make_new_cluster(p, g);
00722     node_induce(p, g);
00723 }
00724 
00725 static int is_empty(graph_t * g)
00726 {
00727     return (!agfstnode(g));
00728 }
00729 
00730 static int is_a_strong_cluster(graph_t * g)
00731 {
00732     int rv;
00733     char *str = agget(g, "compact");
00734     /* rv = mapBool((str), TRUE); */
00735     rv = mapBool((str), FALSE);
00736     return rv;
00737 }
00738 
00739 static int rankset_kind(graph_t * g)
00740 {
00741     char *str = agget(g, "rank");
00742 
00743     if (str && str[0]) {
00744         if (!strcmp(str, "min"))
00745             return MINRANK;
00746         if (!strcmp(str, "source"))
00747             return SOURCERANK;
00748         if (!strcmp(str, "max"))
00749             return MAXRANK;
00750         if (!strcmp(str, "sink"))
00751             return SINKRANK;
00752         if (!strcmp(str, "same"))
00753             return SAMERANK;
00754     }
00755     return NORANK;
00756 }
00757 
00758 static int is_nonconstraint(edge_t * e)
00759 {
00760     char *constr;
00761 
00762     if (E_constr && (constr = agxget(e, E_constr))) {
00763         if (constr[0] && mapbool(constr) == FALSE)
00764             return TRUE;
00765     }
00766     return FALSE;
00767 }
00768 
00769 static node_t *find(node_t * n)
00770 {
00771     node_t *set;
00772     if ((set = ND_set(n))) {
00773         if (set != n)
00774             set = ND_set(n) = find(set);
00775     } else
00776         set = ND_set(n) = n;
00777     return set;
00778 }
00779 
00780 static node_t *union_one(node_t * leader, node_t * n)
00781 {
00782     if (n)
00783         return (ND_set(find(n)) = find(leader));
00784     else
00785         return leader;
00786 }
00787 
00788 static node_t *union_all(graph_t * g)
00789 {
00790     node_t *n, *leader;
00791 
00792     n = agfstnode(g);
00793     if (!n)
00794         return n;
00795     leader = find(n);
00796     while ((n = agnxtnode(g, n)))
00797         union_one(leader, n);
00798     return leader;
00799 }
00800 
00801 static void compile_samerank(graph_t * ug, graph_t * parent_clust)
00802 {
00803     graph_t *s;                 /* subgraph being scanned */
00804     graph_t *clust;             /* cluster that contains the rankset */
00805     node_t *n, *leader;
00806 
00807     if (is_empty(ug))
00808         return;
00809     if (is_a_cluster(ug)) {
00810         clust = ug;
00811         if (parent_clust) {
00812             GD_level(ug) = GD_level(parent_clust) + 1;
00813             set_parent(ug, parent_clust);
00814         }
00815         else
00816             GD_level(ug) = 0;
00817     } else
00818         clust = parent_clust;
00819 
00820     /* process subgraphs of this subgraph */
00821     for (s = agfstsubg(ug); s; s = agnxtsubg(s))
00822         compile_samerank(s, clust);
00823 
00824     /* process this subgraph as a cluster */
00825     if (is_a_cluster(ug)) {
00826         for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
00827             if (ND_clust(n) == 0)
00828                 ND_clust(n) = ug;
00829 #ifdef DEBUG
00830             fprintf(stderr, "(%s) %s  %p\n", agnameof(ug), agnameof(n),
00831                     ND_clust(n));
00832 #endif
00833         }
00834     }
00835 
00836     /* process this subgraph as a rankset */
00837     switch (rankset_kind(ug)) {
00838     case SOURCERANK:
00839         GD_has_sourcerank(clust) = TRUE;        /* fall through */
00840     case MINRANK:
00841         leader = union_all(ug);
00842         GD_minrep(clust) = union_one(leader, GD_minrep(clust));
00843         break;
00844     case SINKRANK:
00845         GD_has_sinkrank(clust) = TRUE;  /* fall through */
00846     case MAXRANK:
00847         leader = union_all(ug);
00848         GD_maxrep(clust) = union_one(leader, GD_maxrep(clust));
00849         break;
00850     case SAMERANK:
00851         leader = union_all(ug);
00852         /* do we need to record these ranksets? */
00853         break;
00854     case NORANK:
00855         break;
00856     default:                    /* unrecognized - warn and do nothing */
00857         agerr(AGWARN, "%s has unrecognized rank=%s", agnameof(ug),
00858               agget(ug, "rank"));
00859     }
00860 
00861     /* a cluster may become degenerate */
00862     if (is_a_cluster(ug) && GD_minrep(ug)) {
00863         if (GD_minrep(ug) == GD_maxrep(ug)) {
00864             node_t *up = union_all(ug);
00865             GD_minrep(ug) = up;
00866             GD_maxrep(ug) = up;
00867         }
00868     }
00869 }
00870 
00871 static graph_t *dot_lca(graph_t * c0, graph_t * c1)
00872 {
00873     while (c0 != c1) {
00874         if (GD_level(c0) >= GD_level(c1))
00875             c0 = GD_parent(c0);
00876         else
00877             c1 = GD_parent(c1);
00878     }
00879     return c0;
00880 }
00881 
00882 static int is_internal_to_cluster(edge_t * e)
00883 {
00884     graph_t *par, *ct, *ch;
00885     ct = ND_clust(agtail(e));
00886     ch = ND_clust(aghead(e));
00887     if (ct == ch)
00888         return TRUE;
00889     par = dot_lca(ct, ch);
00890     /* if (par == agroot(par)) */
00891         /* return FALSE; */
00892     if ((par == ct) || (par == ch))
00893         return TRUE;
00894     return FALSE;
00895 }
00896 
00897 static node_t* Last_node;
00898 static node_t* makeXnode (graph_t* G, char* name)
00899 {
00900     node_t *n = agnode(G, name, 1);
00901     alloc_elist(4, ND_in(n));
00902     alloc_elist(4, ND_out(n));
00903     if (Last_node) {
00904         ND_prev(n) = Last_node;
00905         ND_next(Last_node) = n;
00906     } else {
00907         ND_prev(n) = NULL;
00908         GD_nlist(G) = n;
00909     }
00910     Last_node = n;
00911     ND_next(n) = NULL;
00912     
00913     return n;
00914 }
00915 
00916 static void compile_nodes(graph_t * g, graph_t * Xg)
00917 {
00918     /* build variables */
00919     node_t *n;
00920 
00921     Last_node = NULL;
00922     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00923         if (find(n) == n)
00924             ND_rep(n) = makeXnode (Xg, agnameof(n));
00925     }
00926     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00927         if (ND_rep(n) == 0)
00928             ND_rep(n) = ND_rep(find(n));
00929     }
00930 }
00931 
00932 static void merge(edge_t * e, int minlen, int weight)
00933 {
00934     ED_minlen(e) = MAX(ED_minlen(e), minlen);
00935     ED_weight(e) += weight;
00936 }
00937 
00938 static void strong(graph_t * g, node_t * t, node_t * h, edge_t * orig)
00939 {
00940     edge_t *e;
00941     if ((e = agfindedge(g, t, h)) ||
00942         (e = agfindedge(g, h, t)) || (e = agedge(g, t, h, 0, 1)))
00943         merge(e, ED_minlen(orig), ED_weight(orig));
00944     else
00945         agerr(AGERR, "ranking: failure to create strong constraint edge between nodes %s and %s\n", 
00946             agnameof(t), agnameof(h));
00947 }
00948 
00949 static void weak(graph_t * g, node_t * t, node_t * h, edge_t * orig)
00950 {
00951     node_t *v;
00952     edge_t *e, *f;
00953     static int id;
00954     char buf[100];
00955 
00956     for (e = agfstin(g, t); e; e = agnxtin(g, e)) {
00957         /* merge with existing weak edge (e,f) */
00958         v = agtail(e);
00959         if ((f = agfstout(g, v)) && (aghead(f) == h)) {
00960             return;
00961         }
00962     }
00963     if (!e) {
00964         sprintf (buf, "_weak_%d", id++);
00965         v = makeXnode(g, buf);
00966         e = agedge(g, v, t, 0, 1);
00967         f = agedge(g, v, h, 0, 1);
00968     }
00969     ED_minlen(e) = MAX(ED_minlen(e), 0);        /* effectively a nop */
00970     ED_weight(e) += ED_weight(orig) * BACKWARD_PENALTY;
00971     ED_minlen(f) = MAX(ED_minlen(f), ED_minlen(orig));
00972     ED_weight(f) += ED_weight(orig);
00973 }
00974 
00975 static void compile_edges(graph_t * ug, graph_t * Xg)
00976 {
00977     node_t *n;
00978     edge_t *e;
00979     node_t *Xt, *Xh;
00980     graph_t *tc, *hc;
00981 
00982     /* build edge constraints */
00983     for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
00984         Xt = ND_rep(n);
00985         for (e = agfstout(ug, n); e; e = agnxtout(ug, e)) {
00986             if (is_nonconstraint(e))
00987                 continue;
00988             Xh = ND_rep(find(aghead(e)));
00989             if (Xt == Xh)
00990                 continue;
00991 
00992             tc = ND_clust(agtail(e));
00993             hc = ND_clust(aghead(e));
00994 
00995             if (is_internal_to_cluster(e)) {
00996                 /* determine if graph requires reversed edge */
00997                 if ((find(agtail(e)) == GD_maxrep(ND_clust(agtail(e))))
00998                     || (find(aghead(e)) == GD_minrep(ND_clust(aghead(e))))) {
00999                     node_t *temp = Xt;
01000                     Xt = Xh;
01001                     Xh = temp;
01002                 }
01003                 strong(Xg, Xt, Xh, e);
01004             } else {
01005                 if (is_a_strong_cluster(tc) || is_a_strong_cluster(hc))
01006                     weak(Xg, Xt, Xh, e);
01007                 else
01008                     strong(Xg, Xt, Xh, e);
01009             }
01010         }
01011     }
01012 }
01013 
01014 static void compile_clusters(graph_t* g, graph_t* Xg, node_t* top, node_t* bot)
01015 {
01016     node_t *n;
01017     node_t *rep;
01018     edge_t *e;
01019     graph_t *sub;
01020 
01021     if (is_a_cluster(g) && is_a_strong_cluster(g)) {
01022         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01023             if (agfstin(g, n) == 0) {
01024                 rep = ND_rep(find(n));
01025                 if (!top) top = makeXnode(Xg,TOPNODE);
01026                 agedge(Xg, top, rep, 0, 1);
01027             }
01028             if (agfstout(g, n) == 0) {
01029                 rep = ND_rep(find(n));
01030                 if (!bot)  bot = makeXnode(Xg,BOTNODE);
01031                 agedge(Xg, rep, bot, 0, 1);
01032             }
01033         }
01034         if (top && bot) {
01035             e = agedge(Xg, top, bot, 0, 1);
01036             merge(e, 0, STRONG_CLUSTER_WEIGHT);
01037         }
01038     }
01039     for (sub = agfstsubg(g); sub; sub = agnxtsubg(sub))
01040         compile_clusters(sub, Xg, top, bot);
01041 }
01042 
01043 static void reverse_edge2(graph_t * g, edge_t * e)
01044 {
01045     edge_t *rev;
01046 
01047     rev = agfindedge(g, aghead(e), agtail(e));
01048     if (!rev)
01049         rev = agedge(g, aghead(e), agtail(e), 0, 1);
01050     merge(rev, ED_minlen(e), ED_weight(e));
01051     agdelete(g, e);
01052 }
01053 
01054 static void dfs(graph_t * g, node_t * v)
01055 {
01056     edge_t *e, *f;
01057     node_t *w;
01058 
01059     if (ND_mark(v))
01060         return;
01061     ND_mark(v) = TRUE;
01062     ND_onstack(v) = TRUE;
01063     for (e = agfstout(g, v); e; e = f) {
01064         f = agnxtout(g, e);
01065         w = aghead(e);
01066         if (ND_onstack(w))
01067             reverse_edge2(g, e);
01068         else {
01069             if (ND_mark(w) == FALSE)
01070                 dfs(g, w);
01071         }
01072     }
01073     ND_onstack(v) = FALSE;
01074 }
01075 
01076 static void break_cycles(graph_t * g)
01077 {
01078     node_t *n;
01079 
01080     for (n = agfstnode(g); n; n = agnxtnode(g, n))
01081         ND_mark(n) = ND_onstack(n) = FALSE;
01082     for (n = agfstnode(g); n; n = agnxtnode(g, n))
01083         dfs(g, n);
01084 }
01085 
01086 static void setMinMax (graph_t* g, int doRoot)
01087 {
01088     int c, v;
01089     node_t *n;
01090     node_t* leader;
01091 
01092       /* Do child clusters */
01093     for (c = 1; c <= GD_n_cluster(g); c++)
01094             setMinMax(GD_clust(g)[c], 0);
01095 
01096     if (!GD_parent(g) && !doRoot) // root graph
01097         return;
01098 
01099     GD_minrank(g) = MAXSHORT;
01100     GD_maxrank(g) = -1;
01101     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01102         v = ND_rank(n);
01103         if (GD_maxrank(g) < v)
01104             GD_maxrank(g) = v;
01105         if (GD_minrank(g) > v) {
01106             GD_minrank(g) = v;
01107             leader = n;
01108         }
01109     }
01110     GD_leader(g) = leader;
01111 }
01112 
01113 /* readout_levels:
01114  * Store node rank information in original graph.
01115  * Set rank bounds in graph and clusters
01116  * Free added data structures.
01117  *
01118  * rank2 is called with balance=1, which ensures that minrank=0
01119  */
01120 static void readout_levels(graph_t * g, graph_t * Xg, int ncc)
01121 {
01122     node_t *n;
01123     node_t *xn;
01124     int* minrk = NULL;
01125     int doRoot = 0;
01126 
01127     GD_minrank(g) = MAXSHORT;
01128     GD_maxrank(g) = -1;
01129     if (ncc > 1) {
01130         int i;
01131         minrk = N_NEW(ncc+1,int);
01132         for (i = 1; i <= ncc; i++)
01133             minrk[i] = MAXSHORT;
01134     }
01135     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01136         xn = ND_rep(find(n));
01137         ND_rank(n) = ND_rank(xn);
01138         if (GD_maxrank(g) < ND_rank(n))
01139             GD_maxrank(g) = ND_rank(n);
01140         if (GD_minrank(g) > ND_rank(n))
01141             GD_minrank(g) = ND_rank(n);
01142         if (minrk) {
01143             ND_comp(n) = ND_comp(xn);
01144             minrk[ND_comp(n)] = MIN(minrk[ND_comp(n)],ND_rank(n));
01145         }
01146     }
01147     if (minrk) {
01148         for (n = agfstnode(g); n; n = agnxtnode(g, n))
01149             ND_rank(n) -= minrk[ND_comp(n)];
01150         /* Non-uniform shifting, so recompute maxrank/minrank of root graph */
01151         doRoot = 1;
01152     }
01153     else if (GD_minrank(g) > 0) {  /* should never happen */
01154         int delta = GD_minrank(g);
01155         for (n = agfstnode(g); n; n = agnxtnode(g, n))
01156             ND_rank(n) -= delta;
01157         GD_minrank(g) -= delta;
01158         GD_maxrank(g) -= delta;
01159     }
01160 
01161     setMinMax(g, doRoot);
01162 
01163     /* release fastgraph memory from Xg */
01164     for (n = agfstnode(Xg); n; n = agnxtnode(Xg, n)) {
01165         free_list(ND_in(n));
01166         free_list(ND_out(n));
01167     }
01168 
01169     free(ND_alg(agfstnode(g)));
01170     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01171         ND_alg(n) = NULL;
01172     }
01173     if (minrk)
01174         free (minrk);
01175 }
01176 
01177 static void dfscc(graph_t * g, node_t * n, int cc)
01178 {
01179     edge_t *e;
01180     if (ND_comp(n) == 0) {
01181         ND_comp(n) = cc;
01182         for (e = agfstout(g, n); e; e = agnxtout(g, e))
01183             dfscc(g, aghead(e), cc);
01184         for (e = agfstin(g, n); e; e = agnxtin(g, e))
01185             dfscc(g, agtail(e), cc);
01186     }
01187 }
01188 
01189 static int connect_components(graph_t * g)
01190 {
01191     int cc = 0;
01192     node_t *n;
01193 
01194     for (n = agfstnode(g); n; n = agnxtnode(g, n))
01195         ND_comp(n) = 0;
01196     for (n = agfstnode(g); n; n = agnxtnode(g, n))
01197         if (ND_comp(n) == 0)
01198             dfscc(g, n, ++cc);
01199     if (cc > 1) {
01200         node_t *root = makeXnode(g, ROOT);
01201         int ncc = 1;
01202         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01203             if (ND_comp(n) == ncc) {
01204                 (void) agedge(g, root, n, 0, 1);
01205                 ncc++;
01206             }
01207         }
01208     }
01209     return (cc);
01210 }
01211 
01212 static void add_fast_edges (graph_t * g)
01213 {
01214     node_t *n;
01215     edge_t *e;
01216     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01217         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
01218             elist_append(e, ND_out(n));
01219             elist_append(e, ND_in(aghead(e)));
01220         }
01221     }
01222 }
01223 
01224 static void my_init_graph(Agraph_t *g, Agobj_t *graph, void *arg)
01225 { int *sz = arg; agbindrec(graph,"level graph rec",sz[0],TRUE); }
01226 static void my_init_node(Agraph_t *g, Agobj_t *node, void *arg)
01227 { int *sz = arg; agbindrec(node,"level node rec",sz[1],TRUE); }
01228 static void my_init_edge(Agraph_t *g, Agobj_t *edge, void *arg)
01229 { int *sz = arg; agbindrec(edge,"level edge rec",sz[2],TRUE); }
01230 static Agcbdisc_t mydisc = { {my_init_graph,0,0}, {my_init_node,0,0}, {my_init_edge,0,0} };
01231 
01232 int infosizes[] = {
01233     sizeof(Agraphinfo_t), 
01234     sizeof(Agnodeinfo_t),
01235     sizeof(Agedgeinfo_t)
01236 };
01237 
01238 void dot2_rank(graph_t * g, aspect_t* asp)
01239 {
01240     int ssize;
01241     int ncc, maxiter = INT_MAX;
01242     char *s;
01243 #ifdef ALLOW_LEVELS
01244     attrsym_t* N_level;
01245 #endif
01246     Last_node = NULL;
01247     graph_t *Xg = agopen("level assignment constraints", Agstrictdirected, 0);
01248     agbindrec(Xg,"level graph rec",sizeof(Agraphinfo_t),TRUE);
01249     agpushdisc(Xg,&mydisc,infosizes);
01250 
01251     edgelabel_ranks(g);
01252 
01253     if ((s = agget(g, "nslimit1")))
01254         maxiter = atof(s) * agnnodes(g);
01255     else
01256         maxiter = INT_MAX;
01257 
01258     compile_samerank(g, 0);
01259     compile_nodes(g, Xg);
01260     compile_edges(g, Xg);
01261     compile_clusters(g, Xg, 0, 0);
01262     break_cycles(Xg);
01263     ncc = connect_components(Xg);
01264     add_fast_edges (Xg);
01265 
01266     if (asp) {
01267         init_UF_size(Xg);
01268         initEdgeTypes(Xg);
01269     }
01270 
01271     if ((s = agget(g, "searchsize")))
01272         ssize = atoi(s);
01273     else
01274         ssize = -1;
01275     rank2(Xg, 1, maxiter, ssize);
01276 /* fastgr(Xg); */
01277     readout_levels(g, Xg, ncc);
01278 #ifdef DEBUG
01279     fprintf (stderr, "Xg %d nodes %d edges\n", agnnodes(Xg), agnedges(Xg));
01280 #endif
01281     agclose(Xg);
01282 }
01283 
01284 #endif /* WITH_CGRAPH */
01285 /* end of new ranking code
01286  */