--- /dev/null
+#include "newdot.h"
+
+static void reverse_edge(graph_t *g, edge_t *e)
+{
+ edge_t *rev;
+
+ rev = agfindedge(g,e->head,e->tail);
+ if (!rev) rev = agedge(g,e->head,e->tail);
+ merge(rev,ED_minlen(e),ED_weight(e));
+ agdelete(g,e);
+}
+
+static void dfs(graph_t *g, node_t *v)
+{
+ edge_t *e, *f;
+ node_t *w;
+
+ if (ND_mark(v)) return;
+ ND_mark(v) = TRUE;
+ ND_onstack(v) = TRUE;
+ for (e = agfstout(g,v); e; e = f) {
+ f = agnxtout(g,e);
+ w = e->head;
+ if (ND_onstack(w)) reverse_edge(g,e);
+ else { if (ND_mark(w) == FALSE) dfs(g,w); }
+ }
+ ND_onstack(v) = FALSE;
+}
+
+static void break_cycles(graph_t *g)
+{
+ node_t *n;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_mark(n) = ND_onstack(n) = FALSE;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ dfs(g,n);
+}
--- /dev/null
+#include "newdot.h"
+#include <math.h>
+
+int mapbool(char *str, int defval)
+{
+ if (str && str[0]) {
+ if (!strcasecmp(str,"false")) return FALSE;
+ if (!strcasecmp(str,"true")) return TRUE;
+ return atoi(str);
+ }
+ return defval;
+}
+
+void *zmalloc(size_t nbytes)
+{
+ char *rv = malloc(nbytes);
+ if (nbytes == 0) return 0;
+ if (rv == NULL) {fprintf(stderr, "out of memory\n"); abort();}
+ memset(rv,0,nbytes);
+ return rv;
+}
+
+void *zrealloc(void *ptr, size_t size, size_t elt, size_t osize)
+{
+ void *p = realloc(ptr,size*elt);
+ if (p == NULL && size) {fprintf(stderr, "out of memory\n"); abort();}
+ if (osize < size) memset((char*)p+(osize*elt),'\0',(size-osize)*elt);
+ return p;
+}
+
+int dot_Verbose;
+char *CmdName = "newdot";
+
+void warn3(char *s0, char *s1, char *s2)
+{
+ fprintf(stderr,"%s: ",CmdName);
+ fprintf(stderr,s0,s1,s2);
+}
+
+int gvround(double arg)
+{
+ return (int)(arg + (arg > 0?.5:-.5));
+}
+
+int gvgetint(void *obj, char *str, int defval)
+{
+ char *valstr;
+ int rv;
+ double frac;
+
+ valstr = agget(obj,str);
+ if (valstr && valstr[0]) {
+ if (sscanf(valstr,"%lf%%",&frac)) rv = gvround(frac * defval);
+ else if (!sscanf(valstr,"%d",&rv)) rv = defval;
+ }
+ else rv = defval;
+ return rv;
+}
+
+Agedge_t *agfindedge(Agraph_t *g, Agnode_t *t, Agnode_t *h)
+{
+ return agedge(g,t,h,0,0);
+}
+
+static void my_init_node(Agraph_t *g, Agobj_t *node, void *arg)
+{ int *sz = arg; agbindrec(node,"level node rec",sz[1],TRUE); }
+static void my_init_edge(Agraph_t *g, Agobj_t *edge, void *arg)
+{ int *sz = arg; agbindrec(edge,"level edge rec",sz[2],TRUE); }
+static void my_init_graph(Agraph_t *g, Agobj_t *graph, void *arg)
+{ int *sz = arg; agbindrec(graph,"level graph rec",sz[0],TRUE); }
+static Agcbdisc_t mydisc = { {my_init_graph,0,0}, {my_init_node,0,0}, {my_init_edge,0,0} };
+
+void agautoinit(Agraph_t *g, int graphinfo_size, int nodeinfo_size, int edgeinfo_size)
+{
+ int *s;
+
+ s = N_NEW(3,int); /* until we fix something, this is a storage leak */
+ s[0] = graphinfo_size; s[1] = nodeinfo_size; s[2] = edgeinfo_size;
+ agpushdisc(g,&mydisc,s);
+}
--- /dev/null
+int gvgetint(void *obj, char *str, int defval);
+int gvround(double arg);
+
+extern queue *new_queue(int);
+extern void enqueue(queue *, Agnode_t *);
+extern void enqueue_neighbors(queue *, Agnode_t *, int);
+extern Agnode_t *dequeue(queue *);
+extern void free_queue(queue *);
+extern void *zmalloc(size_t);
+
+extern void start_timer(void);
+extern double elapsed_sec(void);
+
+void warn3(char *s0, char *s1, char *s2);
+
+int crossings_below(Agraph_t *g, int northlevel);
+Agedge_t *agfindedge(Agraph_t *g, Agnode_t *t, Agnode_t *h);
+void agautoinit(Agraph_t *g, int, int, int);
--- /dev/null
+/* Simple and Efficient BiLayer Cross Counting by Barth, Mutzel and Junger */
+typedef struct Agnodeinfo_s {
+ int order;
+} Agnodeinfo_t;
+typedef struct Agedgeinfo_s {
+ int useless;
+} Agedgeinfo_t;
+typedef struct Agraphinfo_s {
+ int useless;
+} Agraphinfo_t;
+#include <cgraph.h>
+#include <radix.h>
+#include <stdlib.h>
+
+#define ND_order(n) ((n)->u.order)
+
+int crossings(int r, int southsequence[], int q)
+{
+ int crosscount, firstindex, *tree, treesize;
+ int t, k, index;
+
+ /* build the accumulator tree */
+ firstindex = 1;
+ while (firstindex<q) firstindex *= 2;
+ treesize = 2*firstindex - 1; /* number of tree nodes */
+ firstindex -= 1; /* index of leftmost leaf */
+ tree = (int *) malloc(treesize*sizeof(int));
+ for (t=0; t<treesize; t++) tree[t] = 0;
+
+ /* count the crossings */
+ crosscount = 0; /* number of crossings */
+ for (k=0; k<r; k++) { /* insert edge k */
+ index = southsequence[k] + firstindex;
+ tree[index]++;
+ while (index > 0) {
+ if (index%2) crosscount += tree[index+1];
+ index = (index - 1)/2;
+ tree[index]++;
+ }
+ }
+ printf("Number of crossings: %d\n",crosscount);
+ free(tree);
+ return crosscount;
+}
+
+static int edgecmpf(void *arg0, void *arg1)
+{
+ int major, minor;
+ Agedge_t *e0, *e1;
+
+ e0 = *(Agedge_t**)arg0;
+ e1 = *(Agedge_t**)arg1;
+ major = ND_order(e0->tail) - ND_order(e1->tail);
+ if (major) return major;
+ minor = ND_order(e0->head) - ND_order(e1->head);
+ return minor;
+}
+
+
+int main(int argc, char **argv)
+{
+ Agraph_t *g;
+ Agnode_t *n;
+ Agedge_t *e, **edgelist;
+ int i, ne, p, q, my_order;
+ int *southseq;
+ char junk;
+
+ aginit();
+ g = agread(stdin); /* must be two-layer */
+ i = ne = p = q = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ sscanf(n->name,"%c%d",&junk,&my_order);
+ ND_order(n) = my_order;
+ if (agfstout(g,n)) p++;
+ else q++;
+ }
+ southseq = malloc(agnnodes(g) * sizeof(int));
+ edgelist = malloc(agnedges(g) * sizeof(Agedge_t*));
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ edgelist[ne++] = e;
+ qsort(edgelist,i,sizeof(Agedge_t*),edgecmpf);
+ for (i = 0; i < ne; i++) southseq[i] = ND_order(edgelist[i]->head);
+ crossings(ne, southseq, q);
+}
--- /dev/null
+typedef struct Agnodeinfo_s {
+ int order;
+} Agnodeinfo_t;
+typedef struct Agedgeinfo_s {
+ int useless;
+} Agedgeinfo_t;
+typedef struct Agraphinfo_s {
+ int useless;
+} Agraphinfo_t;
+#include <cgraph.h>
+#include <stdlib.h>
+#include <radix.h>
+
+#define ND_order(n) ((n)->u.order)
+
+int crossings(int r, int southsequence[], int q)
+{
+ int crosscount, firstindex, *tree, treesize;
+ int t, k, index;
+
+ /* build the accumulator tree */
+ firstindex = 1;
+ while (firstindex<q) firstindex *= 2;
+ treesize = 2*firstindex - 1; /* number of tree nodes */
+ firstindex -= 1; /* index of leftmost leaf */
+ tree = (int *) malloc(treesize*sizeof(int));
+ for (t=0; t<treesize; t++) tree[t] = 0;
+
+ /* count the crossings */
+ crosscount = 0; /* number of crossings */
+ for (k=0; k<r; k++) { /* insert edge k */
+ index = southsequence[k] + firstindex;
+ tree[index]++;
+ while (index > 0) {
+ if (index%2) crosscount += tree[index+1];
+ index = (index - 1)/2;
+ tree[index]++;
+ }
+ }
+ printf("Number of crossings: %d\n",crosscount);
+ free(tree);
+ return crosscount;
+}
+
+static int edgecmpf(void *arg0, void *arg1)
+{
+ int major, minor;
+ Agedge_t *e0, *e1;
+
+ e0 = *(Agedge_t**)arg0;
+ e1 = *(Agedge_t**)arg1;
+ major = ND_order(e0->tail) - ND_order(e1->tail);
+ if (major) return major;
+ minor = ND_order(e0->head) - ND_order(e1->head);
+ return minor;
+}
+
+int main(int argc, char **argv)
+{
+ Agraph_t *g;
+ Agnode_t *n;
+ Agedge_t *e;
+ radixrec_t *edgelist;
+ int i, ne, p, q, my_order;
+ int *southseq;
+ char junk;
+
+ aginit();
+ g = agread(stdin); /* must be two-layer */
+ i = ne = p = q = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ sscanf(n->name,"%c%d",&junk,&my_order);
+ ND_order(n) = my_order;
+ if (agfstout(g,n)) p++;
+ else q++;
+ }
+ southseq = malloc(agnnodes(g) * sizeof(int));
+ edgelist = malloc(agnedges(g) * sizeof(radixrec_t));
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ for (e = agfstout(g,n); e; e = agnxtout(g,e), ne++)
+ { edgelist[ne].key = (ND_order(e->tail) << 16) + ND_order(e->head);
+ edgelist[ne].data = e; }
+ /*qsort(edgelist,i,sizeof(Agedge_t*),edgecmpf);*/
+ radix_sort(edgelist,ne);
+ for (i = 0; i < ne; i++) printf("%d %d %s %s\n",
+ ((edgelist[i].key & 0xffff0000) >> 16),
+ ((edgelist[i].key & 0x0000ffff) >> 0),
+ ((Agedge_t*)(edgelist[i].data))->tail->name,
+ ((Agedge_t*)(edgelist[i].data))->head->name);
+ for (i = 0; i < ne; i++) southseq[i] = ND_order(((Agedge_t*)(edgelist[i].data))->head);
+ crossings(ne, southseq, q);
+}
--- /dev/null
+static int Base = -1; /* zero-based arrays */
+#define Odd(n) ((n) % 2)
+static int CountEdges(int Tree[], int n, int last)
+{
+ int Pos, Sum;
+
+ if (n == last) return 0;
+ Pos = n + Base + 1;
+ Sum = 0;
+ while (Pos >= 1) {
+ if (Odd(Pos))
+ Sum = Sum + Tree[Pos - 1];
+ Pos = Pos / 2;
+ }
+ return (Tree[1 + Base] - Sum);
+}
--- /dev/null
+#include "newdot.h"
+
+/* Simple and Efficient BiLayer Cross Counting by Barth, Mutzel and Junger */
+
+static int count_crossings(rank_t *north, rank_t *south)
+{
+ int crosscount, firstindex;
+ int t, k, index;
+ static int trip;
+
+ trip++;
+ /* build the accumulator tree */
+ if (north->tree == 0) {
+ firstindex = 1;
+ while (firstindex < south->n) firstindex *= 2;
+ north->treesize = 2*firstindex - 1; /* number of tree nodes */
+ north->tree = N_NEW(north->treesize,int);
+ firstindex -= 1;
+ }
+ else {
+ firstindex = (north->treesize + 1) / 2 - 1; /* index of leftmost leaf */
+ }
+ for (t=0; t<north->treesize; t++) north->tree[t] = 0;
+
+ /* count the crossings */
+ crosscount = 0; /* number of crossings */
+ for (k=0; k < north->ne; k++) { /* insert edge k */
+ index = ND_order(aghead(((Agedge_t*)(north->edgelist[k].data)))) + firstindex;
+ north->tree[index]++;
+ while (index > 0) {
+ if (index%2) crosscount += north->tree[index+1];
+ index = (index - 1)/2;
+ north->tree[index]++;
+ }
+ }
+ return crosscount;
+}
+
+int crossings_below(Agraph_t *g, int northlevel)
+{
+ int c;
+ rank_t *r;
+ node_t **v;
+ edge_t *e;
+
+ r = &GD_rank(g)[northlevel];
+ if (r->crossing_cache.valid)
+ return r->crossing_cache.count;
+
+ if (northlevel == GD_maxrank(g)) c = 0;
+ else {
+ if (!r->edgelist) { /* set up edgelist */
+ /* count the edges */
+ c = 0;
+ for (v = r->v; *v; v++)
+ for (e = agfstout(g,*v); e; e = agnxtout(g,e)) c++;
+ r->edgelist = N_NEW(c+1,radixrec_t);
+ /* install the edges */
+ c = 0;
+ for (v = r->v; *v; v++)
+ for (e = agfstout(g,*v); e; e = agnxtout(g,e)) {
+ r->edgelist[c].key = (ND_order(agtail(e)) << 16) + ND_order(aghead(e));
+ r->edgelist[c].data = e;
+ c++;
+ }
+ r->ne = c;
+ }
+ if (r->ne > 0) {
+ radix_sort(r->edgelist,r->ne);
+ c = count_crossings(r,&GD_rank(g)[northlevel+1]);
+ }
+ }
+ r->crossing_cache.count = c;
+ r->crossing_cache.valid = TRUE;
+ return c;
+}
--- /dev/null
+/* this function will have to be modified if we go for
+ * multi-level nodes */
+int is_flat_edge(Agedge_t *e)
+{
+ if (ND_rank(agtail(e)) = ND_rank(aghead(e)) return TRUE;
+ return FALSE;
+}
+
+void model_flat_edge(Agraph_t *flat, Agedge_t *orig)
+{
+ Agnode_t *u, *v;
+ u = model_flat_node(flat, agtail(orig));
+ v = model_flat_node(flat, aghead(orig));
+ e = agedge(flat,u,v,0,TRUE); /* could merge edges if multiple? */
+}
+
+Agraph_t *flat_init(Agraph_t *user)
+{
+ flat = agopen("flatgraph",Agstrictdirected,0);
+ for (u = agfstnode(user); u; u = agnxtnode(user,u)) {
+ for (e = agfstout(user,u); e; e = agnxtout(user,e)) {
+ if (is_flat_edge(e))
+ model_flat_edge(flat,e);
+ }
+ }
+}
+
+void flat_dfs(Agraph_t *flat, Agnode_t *n)
+{
+ ND_mark(n) = TRUE;
+ ND_onstack(n) = TRUE;
+ for (e = agfstout(flat,n); e; e = next_e) {
+ next_e = agnxtout(g,e);
+ if (ND_onstack(aghead(e))) agdelete(flat,e);
+ }
+ ND_onstack(n) = FALSE;
+}
+
+void flat_breakcycles(Agraph_t *flat)
+{
+ for (n = agfstnode(flat); n; n = agnxtnode(flat,n))
+ ND_mark(n) = 0;
+ for (n = agfstnode(flat); n; n = agnxtnode(flat,n))
+ if (!ND_mark(n)) flat_dfs(flat,n);
+}
+
+static void reverse_edge(Agraph_t *g, edge_t *e)
+{
+ edge_t *rev;
+
+ rev = agfindedge(g,aghead(e),agtail(e));
+ if (!rev) rev = agedge(g,aghead(e),agtail(e),(char*)0,TRUE);
+ merge(rev,ED_minlen(e),ED_weight(e));
+ agdelete(g,e);
+}
+
+void flat_phase(Agraph_t *user)
+{
+ flat = flat_init(user);
+ flat_breakcycles(flat);
+}
--- /dev/null
+#include "newdot.h"
+
+static int is_a_cluster(graph_t *g);
+static int is_a_strong_cluster(graph_t *g);
+static void compile_samerank(graph_t *ug, graph_t *parent_clust);
+static int is_internal_to_cluster(edge_t *e);
+static void compile_nodes(graph_t *g, graph_t *Xg);
+static void strong(graph_t *g, node_t *t, node_t *h, edge_t *orig);
+static void weak(graph_t *g, node_t *t, node_t *h, edge_t *orig);
+static void compile_edges(graph_t *ug, graph_t *Xg);
+static void compile_clusters(graph_t *g, graph_t *Xg);
+static void dfs(graph_t *g, node_t *n);
+static void break_cycles(graph_t *g);
+static void readout_levels(graph_t *g, graph_t *Xg);
+static void connect_components(graph_t *g);
+
+void dot_levels(graph_t *g)
+{
+ graph_t *Xg = agopen("level assignment constraints",Agstrictdirected,0);
+
+ agautoinit(Xg,sizeof(Agraphinfo_t),sizeof(Agnodeinfo_t),sizeof(Agedgeinfo_t));
+ compile_samerank(g,0);
+ compile_nodes(g,Xg);
+ compile_edges(g,Xg);
+ compile_clusters(g,Xg);
+ break_cycles(Xg);
+ connect_components(Xg);
+ rank(Xg,1,MAXINT);
+ readout_levels(g,Xg);
+}
+
+static int is_empty(graph_t *g)
+{
+ return (agfstnode(g) == NILnode);
+}
+
+static int is_a_cluster(graph_t *g)
+{
+ return ((g == g->root) || (!strncasecmp(agnameof(g),"cluster",7)));
+}
+
+static int is_a_strong_cluster(graph_t *g)
+{
+ char *str;
+ str = agget(g,"compact");
+ return mapbool((str),FALSE);
+}
+
+static int rankset_kind(graph_t *g)
+{
+ char *str = agget(g,"rank");
+
+ if (str && str[0]) {
+ if (!strcmp(str,"min")) return MINRANK;
+ if (!strcmp(str,"source")) return SOURCERANK;
+ if (!strcmp(str,"max")) return MAXRANK;
+ if (!strcmp(str,"sink")) return SINKRANK;
+ if (!strcmp(str,"same")) return SAMERANK;
+ }
+ return NORANK;
+}
+
+static int is_nonconstraint(edge_t *e)
+{
+ char *str;
+ str = agget(e,"constraint");
+ return mapbool((str),FALSE);
+}
+
+static node_t *find(node_t *n)
+{
+ node_t *set;
+ if ((set = ND_set(n))) {
+ if (set != n) set = ND_set(n) = find(set);
+ }
+ else set = ND_set(n) = n;
+ return set;
+}
+
+static node_t *union_one(node_t *leader, node_t *n)
+{
+ if (n) return (ND_set(find(n)) = find(leader));
+ else return leader;
+}
+
+static node_t *union_all(graph_t *g)
+{
+ node_t *n, *leader;
+
+ n = agfstnode(g);
+ if (!n) return n;
+ leader = find(n);
+ while ((n = agnxtnode(g,n)))
+ union_one(leader,n);
+ return leader;
+}
+
+static void compile_samerank(graph_t *ug, graph_t *parent_clust)
+{
+ graph_t *s; /* subgraph being scanned */
+ graph_t *clust; /* cluster that contains the rankset */
+ node_t *n, *leader;
+
+ if (is_a_cluster(ug)) {
+ clust = ug;
+ GD_parent(ug) = parent_clust;
+ if (parent_clust) GD_level(ug) = GD_level(parent_clust) + 1;
+ else GD_level(ug) = 0;
+ }
+ else clust = parent_clust;
+ if (is_empty(ug)) return;
+
+ /* process subgraphs of this subgraph */
+ for (s = agfstsubg(ug); s; s = agnxtsubg(s))
+ compile_samerank(s,clust);
+
+ /* process this subgraph as a cluster */
+ if (is_a_cluster(ug)) {
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ if (ND_cluster(n) == NILgraph) ND_cluster(n) = ug;
+ }
+ }
+
+ /* process this subgraph as a rankset */
+ switch(rankset_kind(ug)) {
+ case SOURCERANK: GD_has_sourcerank(clust) = TRUE; /* fall through */
+ case MINRANK:
+ leader = union_all(ug);
+ GD_minrep(clust) = union_one(leader, GD_minrep(clust));
+ break;
+ case SINKRANK: GD_has_sinkrank(clust) = TRUE; /* fall through */
+ case MAXRANK:
+ leader = union_all(ug);
+ GD_maxrep(clust) = union_one(leader, GD_maxrep(clust));
+ break;
+ case SAMERANK:
+ leader = union_all(ug);
+ /* do we need to record these ranksets? */
+ break;
+ case NORANK:
+ break;
+ default: /* unrecognized - warn and do nothing */
+ warn3("%s has unrecognized rank=%s",agnameof(ug),agget(ug,"rank"));
+ }
+
+ /* a cluster may become degenerate */
+ if (is_a_cluster(ug) && GD_minrep(ug)) {
+ if (GD_minrep(ug) == GD_maxrep(ug)) {
+ GD_minrep(ug) = GD_maxrep(ug) = union_all(ug);
+ }
+ }
+}
+
+graph_t *dot_lca(graph_t *c0, graph_t *c1)
+{
+ while (c0 != c1) {
+ if (GD_level(c0) >= GD_level(c1))
+ c0 = GD_parent(c0);
+ else c1 = GD_parent(c1);
+ }
+ return c0;
+}
+
+static int is_internal_to_cluster(edge_t *e)
+{
+ graph_t *par,*ct,*ch;
+ ct = ND_cluster(agtail(e));
+ ch = ND_cluster(aghead(e));
+ if (ct == ch) return TRUE;
+ par = dot_lca(ct,ch);
+ if ((par == ct) || (par == ch)) return TRUE;
+ return FALSE;
+}
+
+static void compile_nodes(graph_t *g, graph_t *Xg)
+{
+ /* build variables */
+ node_t *n;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ if (find(n) == n) ND_rep(n) = agnode(Xg,agnameof(n),TRUE);
+ }
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ if (ND_rep(n) == NILnode) ND_rep(n) = ND_rep(find(n));
+ }
+}
+
+static void merge(edge_t *e, int minlen, int weight)
+{
+ ED_minlen(e) = MAX(ED_minlen(e),minlen);
+ ED_weight(e) += weight;
+}
+
+static void strong(graph_t *g, node_t *t, node_t *h, edge_t *orig)
+{
+ edge_t *e;
+ if ((e = agfindedge(g,t,h)) ||
+ (e = agfindedge(g,h,t)) ||
+ (e = agedge(g,t,h,(char*)0,TRUE)))
+ merge(e,ED_minlen(orig),ED_weight(orig));
+ else abort();
+}
+
+static void weak(graph_t *g, node_t *t, node_t *h, edge_t *orig)
+{
+ node_t *v;
+ edge_t *e,*f;
+
+ for (e = agfstin(g,t); e; e = agnxtin(g,e)) {
+ /* merge with existing weak edge (e,f) */
+ v = agtail(e);
+ if ((f = agfstout(g,v)) && (aghead(f) == h)) {
+ return;
+ }
+ }
+ if (!e) {
+ v = agnode(g,0,TRUE);
+ e = agedge(g,v,t,(char*)0,TRUE);
+ f = agedge(g,v,h,(char*)0,TRUE);
+ }
+ ED_minlen(e) = MAX(ED_minlen(e),0); /* effectively a nop */
+ ED_weight(e) += ED_weight(orig) * BACKWARD_PENALTY;
+ ED_minlen(f) = MAX(ED_minlen(f),ED_minlen(orig));
+ ED_weight(f) += ED_weight(orig);
+}
+
+static void compile_edges(graph_t *ug, graph_t *Xg)
+{
+ node_t *n;
+ edge_t *e;
+ node_t *Xt,*Xh;
+ graph_t *tc,*hc;
+
+ /* build edge constraints */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ Xt = ND_rep(n);
+ for (e = agfstout(ug,n); e; e = agnxtout(ug,e)) {
+ if (is_nonconstraint(e)) continue;
+ Xh = ND_rep(find(aghead(e)));
+ if (Xt == Xh) continue;
+
+ tc = ND_cluster(agtail(e));
+ hc = ND_cluster(aghead(e));
+
+ if (is_internal_to_cluster(e)) {
+ /* determine if graph requires reversed edge */
+ if ((find(agtail(e)) == GD_maxrep(ND_cluster(agtail(e))))
+ || (find(aghead(e)) == GD_minrep(ND_cluster(aghead(e))))) {
+ node_t *temp = Xt; Xt = Xh; Xh = temp;
+ }
+ strong(Xg,Xt,Xh,e);
+ }
+ else {
+ if (is_a_strong_cluster(tc) || is_a_strong_cluster(hc))
+ weak(Xg,Xt,Xh,e);
+ else
+ strong(Xg,Xt,Xh,e);
+ }
+ }
+ }
+}
+
+static void compile_clusters(graph_t *g, graph_t *Xg)
+{
+ node_t *n;
+ node_t *rep, *top = 0, *bot = 0;
+ edge_t *e;
+ graph_t *sub;
+
+ if (is_a_cluster(g) && is_a_strong_cluster(g)) {
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ if (agfstin(g,n) == NILedge) {
+ rep = ND_rep(find(n));
+ if (!top) top = agnode(Xg,"\177top",TRUE);
+ agedge(Xg,top,rep,(char*)0,TRUE);
+ }
+ if (agfstout(g,n) == NILedge) {
+ rep = ND_rep(find(n));
+ if (!bot) bot = agnode(Xg,"\177bot",TRUE);
+ agedge(Xg,rep,bot,(char*)0,TRUE);
+ }
+ }
+ if (top && bot) {
+ e = agedge(Xg,top,bot,(char*)0,TRUE);
+ merge(e,0,STRONG_CLUSTER_WEIGHT);
+ }
+ }
+ for (sub = agfstsubg(g); sub; sub = agnxtsubg(sub))
+ compile_clusters(sub,Xg);
+}
+
+static void reverse_edge(graph_t *g, edge_t *e)
+{
+ edge_t *rev;
+
+ rev = agfindedge(g,aghead(e),agtail(e));
+ if (!rev) rev = agedge(g,aghead(e),agtail(e),(char*)0,TRUE);
+ merge(rev,ED_minlen(e),ED_weight(e));
+ agdelete(g,e);
+}
+
+static void dfs(graph_t *g, node_t *v)
+{
+ edge_t *e, *f;
+ node_t *w;
+
+ if (ND_mark(v)) return;
+ ND_mark(v) = TRUE;
+ ND_onstack(v) = TRUE;
+ for (e = agfstout(g,v); e; e = f) {
+ f = agnxtout(g,e);
+ w = aghead(e);
+ if (ND_onstack(w)) reverse_edge(g,e);
+ else { if (ND_mark(w) == FALSE) dfs(g,w); }
+ }
+ ND_onstack(v) = FALSE;
+}
+
+static void break_cycles(graph_t *g)
+{
+ node_t *n;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_mark(n) = ND_onstack(n) = FALSE;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ dfs(g,n);
+}
+
+static void readout_levels(graph_t *g, graph_t *Xg)
+{
+ node_t *n;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ ND_rank(n) = ND_rank(ND_rep(find(n)));
+ }
+}
+
+static void dfscc(graph_t *g, node_t *n, int cc)
+{
+ edge_t *e;
+ if (ND_mark(n) == 0) {
+ ND_mark(n) = cc;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ dfscc(g,aghead(e),cc);
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ dfscc(g,agtail(e),cc);
+ }
+}
+
+static void connect_components(graph_t *g)
+{
+ int cc = 1;
+ node_t *n;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_mark(n) = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ if (ND_mark(n) == 0) dfscc(g,n,cc++);
+ if (cc > 1) {
+ node_t *root = agnode(g,"\177root",TRUE);
+ int ncc = 1;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ if (ND_mark(n) == ncc) {
+ (void) agedge(g,root,n,(char*)0,TRUE);
+ ncc++;
+ }
+ }
+ }
+}
+
+static void aaa(graph_t *g)
+{
+ node_t *n;
+ edge_t *e;
+ Agsym_t *weight, *minlen, *label;
+ char buf[64];
+
+ weight = agattr(g,AGEDGE,"weight","");
+ minlen = agattr(g,AGEDGE,"minlen","");
+ label = agattr(g,AGEDGE,"label","");
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
+ sprintf(buf,"%f",ED_weight(e));
+ agxset(e,weight,buf);
+ sprintf(buf,"%f",ED_minlen(e));
+ agxset(e,minlen,buf);
+ sprintf(buf,"%.3f,%.3f",ED_weight(e),ED_minlen(e));
+ agxset(e,label,buf);
+ }
+ }
+}
+void printgraph(Agraph_t *g) {
+ aaa(g);
+ agwrite(g,stderr);
+}
--- /dev/null
+#include "newdot.h"
+
+void readin_attrs(graph_t *g)
+{
+ node_t *n;
+ edge_t *e;
+ Agsym_t *ap_weight, *ap_minlen;
+
+ ap_weight = agattr(g,AGEDGE,"weight","1");
+ ap_minlen = agattr(g,AGEDGE,"minlen","1");
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
+ ED_weight(e) =atoi(agxget(e,ap_weight));
+ ED_minlen(e) = atoi(agxget(e,ap_minlen));
+ }
+ }
+}
+
+void attach_attributes(graph_t *g)
+{
+ node_t *n;
+ Agsym_t *rank, *order;
+ char buf[64];
+
+ rank = agattr(g,AGNODE,"rank","");
+ order = agattr(g,AGNODE,"order","");
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ sprintf(buf,"%d",ND_rank(n));
+ agxset(n,rank,buf);
+ sprintf(buf,"%d",ND_order(n));
+ agxset(n,order,buf);
+ }
+}
+
+void init_graph(graph_t *g)
+{
+ aginit(g,AGRAPH,"graphviz",sizeof(Agraphinfo_t),TRUE);
+ aginit(g,AGNODE,"graphviz",sizeof(Agnodeinfo_t),TRUE);
+ aginit(g,AGEDGE,"graphviz",sizeof(Agedgeinfo_t),TRUE);
+}
+
+int main(int argc, char *argv[])
+{
+ graph_t *g;
+ FILE *in;
+ if (argc > 1) in = fopen(argv[1],"r");
+ else in = stdin;
+ g = agread(in,0);
+ init_graph(g);
+ readin_attrs(g);
+ dot_levels(g);
+// if (argc <= 2) dot_mincross(g);
+ attach_attributes(g);
+ agwrite(g,stdout);
+ return 1;
+}
--- /dev/null
+#include "newdot.h"
+
+/* given:
+ * ND_rank(v) int integer level assignments
+ * ND_ranksize(v) int number of levels (assumed >= 1)
+ * ED_xpenalty(e) float crossing penalty factor
+ * find:
+ * ND_order(v) int index within rank
+ * ND_pos(v) coord (define: is this the center point?)
+ * ED_pos(e) coord* (define: polyline?)
+ *
+ * todo: node, edge, graph labels, flat edges
+ */
+
+/* internal graph variables:
+ * user graph
+ * ND_cluster(v) graph* lowest containing cluster
+ * GD_model(g) graph* model graph
+ * GD_parent(g) graph* parent cluster
+ * GD_level int distance from layout root
+ *
+ * model graph objects
+ * GD_usergraph(g) graph* original graph or one of its clusters
+ * GD_minrank(g) int lowest rank index incl. external edges
+ * GD_maxrank(g) int highest rank index incl. external edges
+ * GD_minlocalrank(g) int lowest rank index of internal nodes
+ * GD_maxlocalrank(g) int highest rank index of internal nodes
+ * GD_level int distance from layout root
+ * GD_rank(g) rank_t[]
+ * GD_repdict(g) Dict_t* local representatives of user objects
+ * ND_component(v) int connected component number
+ * ND_order(v) int order within rank
+ * ND_type(v) int
+ * NODE, TALLNODE primitive node
+ * EXTNODE represents endpoint of an external cluster edge
+ * SKELETON represents an internal cluster
+ * PATH for virtual nodes in edges of all kinds
+ * ND_cluster(v) graph* for EXTNODE, its lowest user cluster
+ for SKELETON, its user cluster
+ for PATH, NODE, TALLNODE 0?
+ * ND_sortweight(v) for mincross and presort before mincross
+ * ND_sortweight_defined(v) (sortweight undefined if up/down degree==0)
+ * ND_isglobal(v) marks objects for the global merged graph
+ */
+
+/* TODO
+ where do we use port.defined?
+ */
+
+/* forward declarations */
+static void transpose_sweep(Agraph_t* g, int reverse);
+static void mincross_sweep(Agraph_t* g, int dir, boolean reverse);
+static void restorebest(graph_t *g);
+static Agraph_t *globalize(Agraph_t *user);
+static int crossings(graph_t *g);
+static void rec_cluster_init(Agraph_t *ug);
+static int left2right(Agraph_t *g, node_t *v, node_t *w);
+static int in_cross(node_t *v,node_t *w);
+static int out_cross(node_t *v, node_t *w);
+static boolean medians(Agraph_t *g, int r0, int r1);
+static void reorder(graph_t *g, int r, boolean reverse, boolean hasfixed);
+static void savebest(graph_t *g);
+static Agnode_t *choosenode(Agnode_t *fst, Agnode_t *snd);
+static void cluster_ranksetup(Agraph_t *user);
+static void cluster_extents(Agraph_t *g);
+static void cluster_rankstorage(Agraph_t *g);
+static Agnode_t *bindnode(Agraph_t *model, Agnode_t *orignode);
+
+
+/*** basic user<->model association maps ***/
+/* determine canonical order of n0, n1 */
+static void getlowhigh(Agnode_t **n0, Agnode_t **n1)
+{
+ Agnode_t *temp;
+ int d;
+ d = ND_rank(*n1) - ND_rank(*n0);
+ if ((d < 0) || ((d == 0) && (AGID(*n1)< AGID(*n0))))
+ {temp = *n0; *n0 = *n1; *n1 = temp;}
+}
+
+static int inrange(int a, int b, int c)
+{
+ return ((a <= b) && (b <= c));
+}
+
+static int repkeycmp(Dt_t *d, void *arg0, void *arg1, Dtdisc_t *disc)
+{
+ void *key0 = ((repkey_t*)arg0)->key;
+ void *key1 = ((repkey_t*)arg1)->key;
+ int rv;
+#ifdef NOTDEF
+ /* not sure why I coded this - SCN 12/27/2005 */
+ switch(agobjkind(key0)) {
+ case AGNODE:
+ rv = AGID(*(Agnode_t*)key1) - AGID(*(Agnode_t*)key0);
+ break;
+ case AGEDGE:
+ rv = AGID(*(Agedge_t*)key1) - AGID(*(Agedge_t*)key0);
+ break;
+ case AGRAPH:
+ /* in libgraph we don't seem to have graph ids, so use pointer */
+ rv = (unsigned long)key1 - (unsigned long) key0;
+ break;
+ default:
+ rv = 0;
+ }
+#else
+ rv = (int)((unsigned long)key1 - (unsigned long) key0);
+#endif
+ return rv;
+}
+
+static Dtdisc_t Repdisc = {
+ 0, /* pass whole object as key */
+ 0, /* key size and type */
+ -1, /* link offset */
+ (Dtmake_f)0,
+ (Dtfree_f)0,
+ (Dtcompar_f) repkeycmp,
+ (Dthash_f)0,
+ (Dtmemory_f)0,
+ (Dtevent_f)0
+};
+
+static Dict_t *repdict(Agraph_t *model)
+{
+ Dict_t *dict;
+
+ dict = GD_repdict(model);
+ if (!dict) dict = GD_repdict(model) = dtopen(&Repdisc,Dttree);
+ return dict;
+}
+
+static rep_t association(Agraph_t *model, void *obj)
+{
+ Dict_t *dict;
+ repkey_t key, rv, *p;
+
+ dict = repdict(model);
+ key.key = obj; /* i hate when other people code like this */
+ if ((p = dtsearch(dict,&key))) rv = *p;
+ else {rv.val.type = 0; rv.val.p = 0;}
+ return rv.val;
+}
+
+static void associate(Agraph_t *model, void *key, rep_t val)
+{
+ Dict_t *dict;
+ repkey_t *newelt;
+
+ assert(association(model,key).type == 0);
+ dict = repdict(model);
+ newelt = NEW(repkey_t);
+ newelt->key = key;
+ newelt->val = val;
+ dtinsert(dict,newelt);
+ if (association(model,key).type == 0) abort();
+}
+
+/* given a value, find (some) rep (including the key) that maps to it.
+ * for now we'll brute force it. note that val may always be an external
+ * edge path, so we wouldn't necessarily need the full inverse map.
+ */
+static repkey_t invassociation(Agraph_t *model, void *val)
+{
+ Dict_t *dict;
+ repkey_t *p;
+ dict = repdict(model);
+ for (p = dtfirst(dict); p; p = dtnext(dict,p)) {
+ if (p->val.p == val) break;
+ }
+ assert(p);
+ return *p;
+}
+
+typedef struct component_s {
+ int n;
+ node_t **root;
+ int r;
+} component_t;
+
+
+
+/* from level.c - eventually clean this up. */
+static int is_a_cluster(Agraph_t *g)
+{
+ return ((g == g->root) || (!strncasecmp(agnameof(g),"cluster",7)));
+}
+
+/* find the cluster of n that is an immediate child of g */
+static Agraph_t *subclustof(Agnode_t *n, Agraph_t *g)
+{
+ Agraph_t *rv;
+ for (rv = ND_cluster(n); rv && (GD_parent(rv) != g); rv = GD_parent(rv));
+ return rv;
+}
+
+static void *T_array(int low, int high, int size)
+{
+ char *rv;
+
+ rv = calloc((high - low + 1),size);
+ rv = rv - (low * size);
+ return rv;
+}
+
+static void *T_base(void *array, int low, int size)
+{
+ char *rv;
+ rv = array;
+ rv = rv + low * size;
+ return rv;
+}
+
+static void freearray(void *array, int low, int size)
+{
+ free(T_base(array,low,size));
+}
+
+static Agnode_t **nodearray(int low, int high)
+{ return (Agnode_t**) T_array(low,high,sizeof(Agnode_t*)); }
+
+static Agedge_t **edgearray(int low, int high)
+{ return (Agedge_t**) T_array(low,high,sizeof(Agedge_t*)); }
+
+static int *intarray(int low, int high)
+{ return (int*) T_array(low,high,sizeof(int)); }
+
+/* internal functions for model graph construction and maintenance */
+static vpath_t *newpath(Agraph_t *model, Agnode_t *u, int low, Agnode_t *v, int high)
+{
+ vpath_t *path;
+ int i;
+ path = NEW(vpath_t);
+ path->low = low; path->high = high;
+ path->v = nodearray(low, high);
+ path->e = edgearray(low, high);
+ for (i = low; i <= high; i++) {
+ if ((i == low) && u) path->v[i] = u;
+ else if ((i == high) && v) path->v[i] = v;
+ else {
+ path->v[i] = agnode(model,0,TRUE);
+ ND_rank(path->v[i]) = i;
+ }
+ if (i > low) path->e[i-1] = agedge(model,path->v[i-1],path->v[i],0,TRUE);
+ }
+ return path;
+}
+
+static void attributepath(vpath_t *path, int nodetype, Agraph_t *clust)
+{
+ int i;
+ for (i = path->low; i <= path->high; i++) {
+ ND_type(path->v[i]) = nodetype;
+ ND_cluster(path->v[i]) = clust;
+ }
+}
+
+static rep_t model_edge(Agraph_t *model, Agedge_t *orig)
+{
+ Agedge_t *e;
+ Agnode_t *low, *high, *u, *v;
+ port_t lowport, highport;
+ vpath_t *path;
+ rep_t rep;
+ int i;
+
+ rep = association(model,orig);
+ if (rep.type == 0) {
+ low = agtail(orig); high = aghead(orig);
+ getlowhigh(&low,&high);
+ u = bindnode(model,low);
+ assert(u);
+ v = bindnode(model,high);
+ assert(v);
+ path = newpath(model,u,ND_rank(low),v,ND_rank(high));
+ rep.type = PATH;
+ rep.p = path;
+ associate(model,orig,rep);
+ }
+ else path = rep.p;
+
+ /* merge the attributes of orig */
+ for (i = path->low; i < path->high; i++) {
+ e = path->e[i];
+ ED_xpenalty(e) += ED_xpenalty(orig);
+ ED_weight(e) += ED_weight(orig);
+ }
+
+ /* deal with ports. note that ends could be swapped. */
+ if (ND_rank(agtail(orig)) <= ND_rank(aghead(orig))) {
+ lowport = ED_tailport(orig);
+ highport = ED_headport(orig);
+ }
+ else {
+ highport = ED_tailport(orig);
+ lowport = ED_headport(orig);
+ }
+ if (lowport.defined)
+ path->avgtailport = ((path->weight * path->avgtailport) + ED_weight(orig) * lowport.p.x) / (path->weight + ED_weight(orig));
+ if (highport.defined)
+ path->avgheadport = ((path->weight * path->avgheadport) + ED_weight(orig) * highport.p.x) / (path->weight + ED_weight(orig));
+ path->weight += ED_weight(orig);
+ return rep;
+}
+
+/* bind/construct representative of an internal node in a model graph */
+static rep_t model_intnode(Agraph_t *model, Agnode_t *orig)
+{
+ int nr;
+ rep_t rep;
+ Agnode_t *v;
+
+ rep = association(model,orig);
+ if (rep.type) return rep;
+
+ nr = ND_ranksize(orig);
+ if (nr <= 1) { /* simple case */
+ rep.type = NODE;
+ v = rep.p = agnode(model,agnameof(orig),TRUE);
+ ND_rank(v) = ND_rank(orig);
+ }
+ else { /* multi-rank node case */
+ rep.type = TALLNODE;
+ rep.p = newpath(model,NILnode,ND_rank(orig),NILnode,ND_rank(orig)+nr-1);
+ attributepath(rep.p,TALLNODE,0);
+ }
+ associate(model,orig,rep);
+ return rep;
+}
+
+/* bind/construct representative of an external endpoint to a model graph */
+static rep_t model_extnode(Agraph_t *model, Agnode_t *orig)
+{
+ Agnode_t *v;
+ rep_t rep;
+
+ rep = association(model,orig);
+ if (rep.type) return rep;
+
+ /* assume endpoint is represented by one node, even if orig is multi-rank.
+ * also we aren't keeping track of ports yet */
+ rep.p = v = agnode(model,agnameof(orig),TRUE);
+ rep.type = EXTNODE;
+ ND_rank(v) = ND_rank(orig); /* should be ND_rank(orig)+ranksize(orig)? */
+ associate(model,orig,rep);
+ return rep;
+}
+
+/* bind/construct representative of an internal cluster of a model graph */
+static rep_t model_clust(Agraph_t *model, Agraph_t *origclust)
+{
+ rep_t rep;
+ vpath_t *path;
+
+ rep = association(model,origclust);
+ if (rep.type) return rep;
+
+ rep.p = path = newpath(model,NILnode,GD_minrank(origclust),NILnode,GD_maxrank(origclust));
+ rep.type = SKELETON;
+ associate(model,origclust,rep);
+ return rep;
+}
+
+/* helper functions for model_edge */
+static rep_t rep_of_node(Agraph_t *model, Agnode_t *orignode)
+{
+ rep_t rep;
+ if (agcontains(GD_usergraph(model),orignode))
+ rep = model_intnode(model,orignode);
+ else
+ rep = model_extnode(model,orignode);
+ return rep;
+}
+
+static Agnode_t *bindnode(Agraph_t *model, Agnode_t *orignode)
+{
+ rep_t rep;
+ rep = rep_of_node(model,orignode);
+ switch (rep.type) {
+ case NODE: case EXTNODE: return (Agnode_t*)(rep.p);
+ default: return ((vpath_t*)(rep.p))->v[ND_rank(orignode)];
+ }
+}
+
+static int leftmost(Agraph_t *model, int r) { return 0; }
+static int rightmost(Agraph_t *model, int r) {return GD_rank(model)[r].n - 1;}
+
+static void flat_edges(Agraph_t *clust)
+{
+#ifdef NOTDEF
+ for (n = agfstnode(clust); n; n = agnxtnode(clust)) {
+ for (e = agfstedge(root,n); e; e = agnxtedge(root,e,n)) {
+ }
+ }
+ ordered_edges();
+#endif
+}
+
+static void search_component(Agraph_t *g, Agnode_t *n, int c)
+{
+ Agedge_t *e;
+ ND_component(n) = c;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (ND_component(aghead(e)) < 0)
+ search_component(g,aghead(e),c);
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (ND_component(agtail(e)) < 0)
+ search_component(g,agtail(e),c);
+}
+
+static int ND_comp_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_component(*(Agnode_t**)arg1) - ND_component(*(Agnode_t**)arg0);
+}
+
+static component_t build_components(Agraph_t *g, boolean down)
+{
+ component_t rv;
+ node_t *n;
+ int r, rootcnt, compcnt, deg;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_component(n) = -1; /* initialize to unknown component */
+
+ compcnt = 0; rootcnt = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ /* set priority for subsequent BFS to install nodes, and record roots */
+ if (down) deg = ND_indeg(n);
+ else deg = ND_outdeg(n);
+ ND_priority(n) = deg;
+ if (deg == 0) rootcnt++;
+ /* count and mark components */
+ if (ND_component(n) < 0)
+ search_component(g,n,compcnt++);
+ }
+
+ rv.n = compcnt;
+ rv.r = rootcnt;
+ rv.root = N_NEW(rv.r,Agnode_t*);
+ r = 0;
+ /* install roots in root list */
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ if (ND_priority(n) == 0) rv.root[r++] = n;
+ /* sort root list so components are contiguous */
+ qsort(rv.root,rv.n,sizeof(node_t*),ND_comp_cmpf);
+ return rv;
+}
+
+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++;
+}
+
+/*
+ populates rank lists of g. there are some key details:
+ 1) the input graph ordering must be respected (in left to right initialization)
+ 2) connected components are separated and marked with indices
+ 3) series-parallel graphs (includes trees, obviously) must not have crossings
+*/
+static void build_ranks(Agraph_t *ug, boolean down)
+{
+ queue *q;
+ component_t c;
+ int r;
+ Agnode_t *n;
+ Agedge_t *e;
+ Agraph_t *g;
+
+ g = GD_model(ug);
+ c = build_components(g, down);
+
+ /* process each each component */
+ q = new_queue(agnnodes(g)+1);
+ for (r = 0; r < c.r; r++) {
+ enqueue(q,c.root[r]);
+ if ((r + 1 >= c.r)||(ND_component(c.root[r])!=ND_component(c.root[r+1]))) {
+ while ((n = dequeue(q))) {
+ install(g,n);
+ if (down) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (--ND_priority(aghead(e)) == 0) enqueue(q,aghead(e));
+ }
+ else {
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (--ND_priority(agtail(e)) == 0) enqueue(q,aghead(e));
+ }
+ }
+ }
+ }
+ free_queue(q);
+}
+
+/* this predicate indicates if mincross should be run on this cluster */
+static boolean run(Agraph_t *mg)
+{
+ if (GD_pass(mg) > GD_maxpass(mg)) return FALSE;
+ if (GD_pass(mg) - GD_lastwin(mg) > GD_mintry(mg)) return FALSE;
+ GD_pass(mg) = GD_pass(mg) + 1;
+ return TRUE;
+}
+
+static void set_presortkey(Agnode_t *n)
+{
+ int key;
+ vpath_t *skel;
+ Agraph_t *model, *parmodel;
+ Agnode_t *orignode, *parnode;
+
+ model = agraphof(n);
+ parmodel = GD_model(GD_parent(GD_usergraph(model)));
+ skel = association(parmodel,GD_usergraph(model)).p;
+ switch(ND_type(n)) {
+ case EXTNODE:
+ orignode = association(model,n).p;
+ parnode = association(parmodel,orignode).p;
+ if ((skel->low <= ND_rank(n)) && (ND_rank(n) <= skel->high)) {
+ int r = ND_rank(n);
+ if (ND_order(parnode) < ND_order(skel->v[r]))
+ key = ND_order(GD_rank(model)[r].v[leftmost(model,ND_rank(n))]) -
+ (ND_order(skel->v[ND_rank(n)]) - ND_order(parnode));
+ break;
+ }
+ else {
+ key = ND_order(parnode);
+ }
+ break;
+ default:
+ key = ND_order(n);
+ break;
+ }
+ ND_sortweight(n) = key;
+ ND_sortweight_defined(n) = TRUE;
+}
+
+/* helper function */
+static int presort_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_sortweight(*(Agnode_t**)arg1) - ND_sortweight(*(Agnode_t**)arg0);
+}
+
+/*
+ * sort the nodes of a subgraph so EXTNODEs are adjusted
+ * according to the parent cluster before the main mincross
+ * phase runs.
+ */
+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++) {
+ for (i = leftmost(mg,r); i < rightmost(mg,r); i++)
+ set_presortkey(GD_rank(mg)[r].v[i]);
+ 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;
+ }
+}
+
+/* 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;
+ repkey_t *ent;
+ Dict_t *d;
+ double frac;
+
+ /* walk all the paths */
+ model = GD_model(ug);
+ d = repdict(model);
+ for (ent = dtfirst(d); ent; ent = dtnext(d,ent)) {
+ if (ent->val.type != PATH) continue;
+ p = ent->val.p;
+ if ((ND_type(p->v[p->low])) == SKELETON) {
+ 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 * PORTRANGE * (frac - 0.5) + p->avgtailport;
+ }
+ if ((ND_type(p->v[p->high])) == SKELETON) {
+ 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 * PORTRANGE * (frac - 0.5) + p->avgheadport;
+ }
+ }
+}
+
+static void mincross_clust(Agraph_t *ug)
+{
+ Agraph_t *g;
+ g = GD_model(ug);
+ if (run(g)) {
+ presort(ug); /* move the external nodes */
+ subclustports(ug);
+ do {
+ mincross_sweep(g,GD_pass(g)%2,GD_pass(g)%4<2);
+ } while (run(g));
+ transpose_sweep(g,TRUE);
+ restorebest(g);
+ }
+}
+
+static void globalopt(Agraph_t *root)
+{
+ Agraph_t *glob;
+
+ glob = globalize(root);
+ fprintf(stderr,"%s: %d crossings\n",agnameof(root),crossings(glob));
+}
+
+/* this assumes one level per node - no mega-nodes */
+static void apply_model(Agraph_t *ug)
+{
+ Agnode_t *un;
+ rep_t rep;
+ for (un = agfstnode(ug); un; un = agnxtnode(ug,un)) {
+ rep = association(GD_globalgraph(ug),un);
+ switch (rep.type) {
+ case NODE:
+ ND_order(un) = ND_order((Agnode_t*)(rep.p));
+ break;
+ case TALLNODE:
+ ND_order(un) = ND_order(((vpath_t*)rep.p)->v[0]);
+ break;
+ default:
+ abort();
+ break;
+ }
+ }
+}
+
+/* this is a first cut at a top-level planner. it's lame. */
+static void rec_cluster_run(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) mincross_clust(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(subg))
+ rec_cluster_run(subg);
+ if (is_a_cluster(ug)) mincross_clust(ug);
+}
+
+/* this is the top level mincross entry point */
+void dot_mincross(Agraph_t *user)
+{
+ rec_cluster_init(user);
+ rec_cluster_run(user);
+ globalopt(user);
+ apply_model(user);
+}
+
+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;
+}
+
+/* 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);
+}
+
+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;
+}
+
+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 mincross_sweep(Agraph_t* g, int dir, boolean reverse)
+{
+ int r,other,low,high,first,last;
+ int hasfixed;
+
+ low = GD_minrank(g);
+ high = GD_maxrank(g);
+ if (dir == 0) return;
+ if (dir > 0) { first = low + 1; last = high; dir = 1;} /* down */
+ else { first = high - 1; last = low; dir = -1;} /* up */
+
+ for (r = first; r != last + dir; r += dir) {
+ other = r - dir;
+ hasfixed = medians(g,r,other);
+ reorder(g,r,reverse,hasfixed);
+ }
+ transpose_sweep(g,NOT(reverse));
+ savebest(g);
+}
+
+
+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;
+}
+
+static void build_flat_graphs(Agraph_t *g)
+{
+}
+
+static int out_cross(node_t *v, node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0,t;
+
+ for (e2 = agfstout(agraphof(w),w); e2; e2 = agnxtout(agraphof(w),e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(aghead(e2));
+ for (e1 = agfstout(agraphof(v),v); e1; e1 = agnxtout(agraphof(v),e1)) {
+ t = ND_order(aghead(e1)) - inv;
+ if ((t > 0) || ((t == 0) && (ED_headport(e1).p.x > ED_headport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int in_cross(node_t *v,node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0, t;
+
+ for (e2 = agfstin(agraphof(w),w); e2; e2 = agnxtin(agraphof(w),e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(agtail(e2));
+ for (e1 = agfstin(agraphof(v),v); e1; e1 = agnxtin(agraphof(v),e1)) {
+ t = ND_order(agtail(e1)) - inv;
+ if ((t > 0) || ((t == 0) && (ED_tailport(e1).p.x > ED_tailport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int int_cmpf(const void *arg0, const void *arg1)
+{
+ return *(int*)arg1 - *(int*)arg0;
+}
+
+/* 8 is the number of bits in port.order, an unsigned char */
+#define VAL(node,port) ((ND_order(node) << 8) + (port).order)
+
+/*
+ * 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(aghead(e),ED_headport(e));}
+ else for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ {if (ED_xpenalty(e) > 0) list[j++] = VAL(agtail(e),ED_tailport(e));}
+ switch(j) {
+ case 0:
+ ND_sortweight(n) = MAXINT; /* no neighbor - median undefined */
+ ND_sortweight_defined(n) = FALSE;
+ break;
+ case 1:
+ ND_sortweight(n) = list[0];
+ ND_sortweight_defined(n) = TRUE;
+ break;
+ case 2:
+ ND_sortweight(n) = (list[0] + list[1])/2;
+ ND_sortweight_defined(n) = TRUE;
+ 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);
+ }
+ }
+ ND_sortweight_defined(n) = TRUE;
+ }
+ }
+#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 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_defined(*lp))) 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_defined(*rp)) 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 savebest(graph_t *g)
+{
+ int nc;
+ Agnode_t *n;
+
+ nc = crossings(g);
+ if (nc < GD_bestcrossings(g)) {
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_saveorder(n) = ND_order(n);
+ GD_bestcrossings(g) = nc;
+ GD_lastwin(g) = GD_pass(g);
+ }
+}
+
+static int ND_order_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_order(*(Agnode_t**)arg1) - ND_order(*(Agnode_t**)arg0);
+}
+
+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 int crossings(graph_t *g)
+{
+ int i, rv;
+
+ rv = 0;
+ for (i = GD_minrank(g); i < GD_maxrank(g); i++) {
+ rv += crossings_below(g,i);
+ }
+ return rv;
+}
+
+/* support function for globalize. merge two external edge vnode vectors.
+ * generally, we will prefer the vnode from the lowest (closest to a leaf)
+ * cluster, but there can be ties so we work from both ends of the vector
+ * to get symmetry.
+ */
+static void mergevec(Agnode_t **v0, Agnode_t **v1, int low, int high)
+{
+ int i,j;
+
+ i = low;
+ j = high;
+ while (i <= j) {
+ v0[i] = choosenode(v0[i],v1[i]);
+ v0[j] = choosenode(v1[j],v0[j]);
+ i++;
+ j--;
+ }
+}
+
+/* see above. fst has to win ties. */
+static Agnode_t *choosenode(Agnode_t *fst, Agnode_t *snd)
+{
+ if (GD_level(agraphof(fst)) <= GD_level(agraphof(snd))) return fst;
+ return snd;
+}
+
+/* get the vnodes of an edge to an EXTNODE but where the
+ * edge 'leaves' the cluster, ascend to its parent.
+ * I am not sure this is really necessary or if it would
+ * suffice to just take the vpath of the edge.
+ */
+static Agnode_t **peel(Agraph_t *model, vpath_t *path)
+{
+ Agnode_t **rv, *v;
+ Agraph_t *pmodel;
+ int r;
+ repkey_t repkey;
+ Agedge_t *origedge;
+ vpath_t *parpath;
+
+ rv = nodearray(path->low, path->high);
+ for (r = path->low; r <= path->high; r++) {
+ v = path->v[r];
+ if (inrange(GD_minrank(model),r,GD_maxrank(model))) rv[r] = v;
+ else {
+ pmodel = GD_parent(model);
+ while (!inrange(GD_minrank(pmodel),r,GD_maxrank(model)) )
+ model = GD_parent(model);
+ /* we seem to be assuming 1-1 relationship between origedges and vpath */
+ repkey = invassociation(model,path);
+ origedge = repkey.key;
+ parpath = association(pmodel,origedge).p;
+ v = parpath->v[r];
+ rv[r] = v;
+ }
+ }
+ return rv;
+}
+
+/* helper function for globalize */
+static void installglobalobjects(Agraph_t *src, Agraph_t *dst, int r)
+{
+ int i;
+ Agnode_t *v, *vx;
+ Agraph_t *clustmodel;
+
+ for (i = leftmost(src,r); i <= rightmost(src,r); i++) {
+ v = GD_rank(src)[r].v[i];
+ if ((vx = ND_globalobj(v)))
+ install(dst,vx);
+ else {
+ if (ND_type(v) == SKELETON) {
+ clustmodel = GD_model(ND_cluster(v));
+ installglobalobjects(clustmodel,dst,r);
+ }
+ else {} /* ignore various external edges */
+ }
+ }
+}
+
+/* build the global (flat) graph of the universe. this is 'flat'
+in the sense that there is one data structure for the entire graph
+(not 'flat' in the sense of flat edges within the same level.)
+*/
+static Agraph_t *globalize(Agraph_t *user)
+{
+ Agraph_t *glob;
+ Agnode_t *v;
+ vpath_t *path, *globpath;
+ int i, r;
+ Agnode_t *n;
+ Agedge_t *e;
+ rep_t rep;
+ rep_t globrep, trep, hrep;
+ Agraph_t *model, *tmodel, *hmodel;
+ Agnode_t **tvec, **hvec;
+
+ glob = agopen("globalgraph",Agstrictdirected,0);
+ GD_usergraph(glob) = user;
+
+ /* mark the global objects from various clusters */
+ for (n = agfstnode(user); n; n = agnxtnode(user,n)) {
+ globrep = model_intnode(glob, n);
+ model = GD_model(ND_cluster(n));
+ rep = association(model,n);
+ switch(rep.type) {
+ case NODE:
+ ND_globalobj(v = rep.p) = globrep.p;
+ break;
+ case TALLNODE:
+ path = rep.p;
+ globpath = globrep.p;
+ for (i = path->low; i < path->high; i++)
+ ND_globalobj(path->v[i]) = globpath->v[i];
+ break;
+ default: abort();
+ }
+ }
+ for (n = agfstnode(user); n; n = agnxtnode(user,n)) {
+ for (e = agfstout(user,n); e; e = agnxtout(user,e)) {
+ globrep = model_edge(glob,e);
+ tmodel = GD_model(ND_cluster(agtail(e)));
+ hmodel = GD_model(ND_cluster(aghead(e)));
+ if (tmodel == hmodel) { /* easy case */
+ rep = association(tmodel,e);
+ assert(rep.type == PATH);
+ path = rep.p;
+ globpath = globrep.p;
+ for (i = path->low; i < path->high; i++)
+ ND_globalobj(path->v[i]) = globpath->v[i];
+ }
+ else {
+ trep = association(tmodel,e);
+ tvec = peel(tmodel,trep.p);
+ hrep = association(hmodel,e);
+ hvec = peel(hmodel,hrep.p);
+ path = (vpath_t*)(trep.p);
+ mergevec(tvec,hvec,path->low,path->high);
+ globpath = globrep.p;
+ for (i = path->low; i <= path->high; i++)
+ ND_globalobj(tvec[i]) = globpath->v[i];
+ freearray(tvec,path->low,sizeof(tvec[0]));
+ freearray(hvec,path->low,sizeof(hvec[0]));
+ }
+ }
+ }
+
+ cluster_extents(glob);
+ cluster_rankstorage(glob);
+
+ /* install new representative objects */
+ for (r = GD_minrank(glob); r <= GD_maxrank(glob); r++)
+ installglobalobjects(GD_model(user),glob,r);
+ GD_globalgraph(user) = glob;
+ return glob;
+}
+
+static void countup(Agraph_t *g, rank_t *globr)
+{
+ Agnode_t *n;
+ Agedge_t *e;
+ int r0, r1, low, high, i;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ for (i = 0; i < ND_ranksize(n); i++)
+ globr[ND_rank(n)+i].n += 1;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
+ r0 = ND_rank(agtail(e));
+ r1 = ND_rank(aghead(e));
+ low = MIN(r0,r1);
+ high = MAX(r0,r1);
+ for (i = low + 1; i < high; i++)
+ globr[i].n += 1;
+ }
+ }
+}
+
+/* --------- */
+
+static void rec_model_subclusts(Agraph_t *model, Agraph_t *user)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(user))
+ (void) model_clust(model,user);
+ /* note there can be non-cluster subgraphs that contain lower clusters */
+ for (subg = agfstsubg(user); subg; subg = agnxtsubg(subg))
+ rec_model_subclusts(model,subg);
+}
+
+static void cluster_contents(Agraph_t *ug)
+{
+ Agraph_t *mg, *root, *subg;
+ char *name;
+ Agnode_t *n;
+ Agedge_t *e;
+
+ assert(is_a_cluster(ug));
+ assert(GD_model(ug) == 0);
+
+ name = malloc(strlen(agnameof(ug))+10);
+ sprintf(name,"model_%s",agnameof(ug));
+ GD_model(ug) = mg = agopen(name,Agstrictdirected,0);
+ free(name);
+ GD_usergraph(mg) = ug;
+ GD_level(mg) = GD_level(ug);
+
+ /* install internal nodes */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ if (ND_cluster(n) == ug)
+ model_intnode(mg,n);
+ }
+
+ /* install cluster skeletons */
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(subg))
+ rec_model_subclusts(mg,subg);
+
+ /* install external edge endpoints */
+ root = ug->root;
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(root,n); e; e = agnxtout(root,e)) {
+ if (!agcontains(ug,agtail(e)))
+ model_extnode(mg,agtail(e));
+ if (!agcontains(ug,aghead(e)))
+ model_extnode(mg,aghead(e));
+ }
+ }
+
+ /* install edges */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(root,n); e; e = agnxtout(root,e)) {
+ model_edge(mg,e); /* amazing if this is all it takes */
+ }
+ /* also need to scan in-edges not seen above */
+ for (e = agfstin(root,n); e; e = agnxtin(root,e)) {
+ if (!agcontains(ug,agtail(e)))
+ model_edge(mg,e);
+ }
+ }
+}
+
+static void cluster_init(Agraph_t *ug)
+{
+ cluster_contents(ug);
+ cluster_ranksetup(ug);
+ flat_edges(ug);
+ build_ranks(ug,TRUE);
+}
+
+static void rec_cluster_init(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) cluster_init(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(subg))
+ rec_cluster_init(subg);
+}
+
+#ifdef NOTDEF
+/* a given edge can have several other edges (forward or backward)
+ between the same endpoints. here, we choose one of these to be
+ the canonical representative of those edges. */
+static Agedge_t* canonical_edge(Agedge_t* e)
+{
+ Agraph_t *g;
+ Agedge_t *canon;
+
+ g = agraphof(e);
+ if (ND_rank(aghead(e)) > ND_rank(agtail(e)))
+ canon = agfindedge(g,agtail(e),aghead(e));
+ else {
+ if
+ }
+}
+#endif
+
+static void cluster_extents(Agraph_t *g)
+{
+ Agnode_t *n;
+ Agedge_t *e;
+ short indeg, outdeg;
+
+ /* find minrank, maxrank, in/out/max node degrees */
+ n = agfstnode(g);
+ GD_minrank(g) = GD_maxrank(g) = ND_rank(n);
+ GD_maxinoutdeg(g) = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ if (GD_maxrank(g) < ND_rank(n)) GD_maxrank(g) = ND_rank(n);
+ if (GD_minrank(g) > ND_rank(n)) GD_minrank(g) = ND_rank(n);
+ if (ND_indeg(n) == 0) {
+ indeg = 0; for (e = agfstin(g,n); e; e = agnxtin(g,e)) indeg++;
+ ND_indeg(n) = indeg;
+ }
+ if (ND_outdeg(n) == 0) {
+ outdeg = 0; for (e = agfstout(g,n); e; e = agnxtout(g,e)) outdeg++;
+ ND_outdeg(n) = outdeg;
+ }
+ GD_maxinoutdeg(g) = MAX(GD_maxinoutdeg(g),MAX(indeg,outdeg));
+ }
+}
+
+/* utility function to allocate GD_rank and GD_rank(g)[r].v */
+static void cluster_rankstorage(Agraph_t *g) /* g is a model graph */
+{
+ Agnode_t *n;
+ int minr,maxr,r,*ranksize;
+
+ /* initialize storage for ranks in the model; nodes are installed later */
+ minr = GD_minrank(g);
+ maxr = GD_maxrank(g);
+ ranksize = intarray(minr,maxr);
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ranksize[ND_rank(n)]++;
+ GD_rank(g) = T_array(minr,maxr,sizeof(rank_t));
+ for (r = minr; r <= maxr; r++)
+ GD_rank(g)[r].v = N_NEW(ranksize[r]+1,Agnode_t*); /* NIL at end */
+ freearray(ranksize,minr,sizeof(ranksize[0]));
+}
+
+/* set GD_minrank, GD_maxrank, ND_indeg, ND_outdeg, GD_maxinoutdeg
+ * allocate GD_rank and GD_rank(g)[r].v
+ */
+static void cluster_ranksetup(Agraph_t *ug)
+{
+ Agraph_t *g;
+
+ g = GD_model(ug);
+ if (agfstnode(g) == NILnode) return;
+
+ cluster_extents(g);
+ cluster_rankstorage(g);
+
+ /* set up mincross running parameters */
+ GD_pass(g) = 0;
+ GD_lastwin(g) = 0;
+ GD_mintry(g) = gvgetint(ug,"minpass",24);
+ GD_maxpass(g) = gvgetint(ug,"maxpass",1024);
+ GD_bestcrossings(g) = MAXINT;
+}
+
+static int printfn(Dict_t *dict, void *arg, void *data)
+{
+ repkey_t *rk = arg;
+ printf("%lx=>%d,%lx\n",(long unsigned int)(rk->key),rk->val.type,(long unsigned int)(rk->val.p));
+ return 0;
+}
+static void dumpdict(Agraph_t *model)
+{
+ dtwalk(repdict(model),printfn, 0);
+}
--- /dev/null
+/*********** external interfaces *********/
+#ifdef NOTDEF
+#include <vmalloc.h>
+#endif
+#include <assert.h>
+#include <string.h>
+//#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+/* #include <values.h> */
+#include <cdt.h>
+#include <cgraph.h>
+#include "radix.h"
+
+/*********** a few useful types *********/
+typedef unsigned char boolean;
+typedef struct {double x, y;} point;
+typedef struct {point LL, UR;} box;
+
+typedef struct Agraph_s graph_t;
+typedef struct Agnode_s node_t;
+typedef struct Agedge_s edge_t;
+
+/*********** types needed by graphs *********/
+typedef struct adjmatrix_t { /* for flat edge constraints */
+ int nrows,ncols;
+ char *data;
+} adjmatrix_t;
+#define ELT(M,i,j) (M->data[((i)*M->ncols)+(j)])
+
+typedef struct rank_s { /* describes the contents of a rank */
+ node_t **v; /* the vertex list */
+ int n; /* its extent */
+
+ /* for counting edge crossings */
+ radixrec_t *edgelist; /* list of out (south) edges */
+ int ne; /* extent of this list */
+ int *tree, treesize; /* the crossing accumulator tree */
+
+ adjmatrix_t *flat; /* transitive closure of the left-right constraints */
+
+ struct crossing_cache_s {
+ int count; /* crossings between this level and this + 1 */
+ boolean valid;
+ } crossing_cache;
+
+ boolean candidate; /* true if an adjacent level changed */
+ boolean changed;
+} rank_t;
+
+typedef struct rep_s {
+ void *p; /* must always be an Agraph_t*, Agnode_t*, Agedge_t* */
+ int type; /* NODE, TALLNODE, PATH, SKELETON, EXTNODE */
+} rep_t;
+
+typedef struct repkey_s {
+ void *key;
+ rep_t val;
+} repkey_t;
+
+typedef struct vpath_s {
+ int low, high;
+ node_t **v; /* virtual nodes of path */
+ edge_t **e; /* virtual edges of path indexed by tail rank */
+ float avgtailport, avgheadport; /* total port offsets */
+ float weight;
+} vpath_t;
+
+
+/*********** graphs *********/
+typedef struct Agraphinfo_s {
+ Agrec_t hdr;
+ struct Agraph_s *parent; /* containing cluster (not parent subgraph) */
+ union {
+ struct Agraph_s *user; /* points from a model graph back to client obj */
+ struct Agraph_s *global; /* points from user root graph to global model */
+ } graph;
+ int level; /* cluster nesting level (not node level!) */
+ node_t *minrep, *maxrep; /* set leaders for min and max rank */
+ boolean has_sourcerank, has_sinkrank;
+ int minrank,maxrank;
+
+ /* these control mincross */
+ struct Agraph_s *graphrep; /* graph that models this cluster for mincross */
+ rank_t *rank;
+ int pass; /* a simple counter */
+ int lastwin; /* last pass that was an improvement */
+ int mintry; /* limit to losing passes */
+ int maxpass; /* limit to total passes */
+ int bestcrossings; /* current best solution */
+ Dict_t *repdict;
+
+ /* other mincross values */
+ int maxinoutdeg; /* maximum of all in and out degrees */
+} Agraphinfo_t;
+
+
+#define GD_FIELD(g,field) (((Agraphinfo_t*)((g)->base.data))->field)
+#define GD_parent(g) GD_FIELD(g,parent)
+#define GD_level(g) GD_FIELD(g,level)
+#define GD_minrep(g) GD_FIELD(g,minrep)
+#define GD_maxrep(g) GD_FIELD(g,maxrep)
+#define GD_has_sourcerank(g) GD_FIELD(g,has_sourcerank)
+#define GD_has_sinkrank(g) GD_FIELD(g,has_sinkrank)
+#define GD_model(g) GD_FIELD(g,graphrep)
+#define GD_rank(g) GD_FIELD(g,rank)
+#define GD_minrank(g) GD_FIELD(g,minrank)
+#define GD_maxrank(g) GD_FIELD(g,maxrank)
+#define GD_pass(g) GD_FIELD(g,pass)
+#define GD_lastwin(g) GD_FIELD(g,lastwin)
+#define GD_mintry(g) GD_FIELD(g,mintry)
+#define GD_maxpass(g) GD_FIELD(g,maxpass)
+#define GD_bestcrossings(g) GD_FIELD(g,bestcrossings)
+#define GD_maxinoutdeg(g) GD_FIELD(g,maxinoutdeg)
+#define GD_repdict(g) GD_FIELD(g,repdict)
+#define GD_usergraph(g) GD_FIELD(g,graph.user)
+#define GD_globalgraph(g) GD_FIELD(g,graph.global)
+
+typedef struct nlist_s {
+ int size;
+ node_t **list;
+} nlist_t;
+
+typedef struct elist_s {
+ int size;
+ edge_t **list;
+} elist_t;
+
+typedef struct queue {
+ node_t **store,**limit,**head,**tail;
+} queue;
+
+/*********** nodes *********/
+typedef struct Agnodeinfo_s {
+ Agrec_t hdr;
+ int rank; /* network simplex solver */
+ graph_t *cluster; /* lowest containing cluster */
+ node_t *set; /* for union-find */
+
+ union {
+ node_t *v;
+ edge_t *e;
+ graph_t *g;
+ } rep;
+ int type;
+
+/* tags for representative (model) objects */
+/* 0 is reserved for undefined */
+#define NODE 1 /* a real node on only one rank */
+#define TALLNODE 2 /* a real node on two or more ranks */
+#define EXTNODE 3 /* a node external to a cluster */
+#define SKELETON 4
+#define PATH 5
+
+ /* for network-simplex */
+ elist_t tree_in;
+ elist_t tree_out;
+ int priority;
+ int low,lim;
+ edge_t *par;
+
+ /* for mincross */
+ short indeg, outdeg;
+ int order;
+ int saveorder; /* for saving the best solution */
+ int component;
+ int ranksize; /* height as counted in number of levels */
+ int sortweight; /* median, barycenter, etc. */
+ boolean mark, onstack, sortweight_defined;
+} Agnodeinfo_t;
+
+#define ND_FIELD(n,field) (((Agnodeinfo_t*)((n)->base.data))->field)
+#define ND_globalobj(n) ND_FIELD(n,rep.v)
+#define ND_rep(n) ND_FIELD(n,rep.v)
+#define ND_cluster(n) ND_FIELD(n,cluster)
+#define ND_cluster(n) ND_FIELD(n,cluster)
+#define ND_type(n) ND_FIELD(n,type)
+#define ND_set(n) ND_FIELD(n,set)
+#define ND_mark(n) ND_FIELD(n,mark)
+#define ND_onstack(n) ND_FIELD(n,onstack)
+#define ND_rank(n) ND_FIELD(n,rank)
+#define ND_tree_in(n) ND_FIELD(n,tree_in)
+#define ND_tree_out(n) ND_FIELD(n,tree_out)
+#define ND_priority(n) ND_FIELD(n,priority)
+#define ND_low(n) ND_FIELD(n,low)
+#define ND_lim(n) ND_FIELD(n,lim)
+#define ND_par(n) ND_FIELD(n,par)
+#define ND_indeg(n) ND_FIELD(n,indeg)
+#define ND_outdeg(n) ND_FIELD(n,outdeg)
+#define ND_order(n) ND_FIELD(n,order)
+#define ND_saveorder(n) ND_FIELD(n,saveorder)
+#define ND_component(n) ND_FIELD(n,component)
+#define ND_sortweight(n) ND_FIELD(n,sortweight)
+#define ND_sortweight_defined(n) ND_FIELD(n,sortweight_defined)
+#define ND_ranksize(n) ND_FIELD(n,ranksize)
+
+/********** edges **********/
+typedef struct port_s { /* internal edge endpoint specification */
+ point p; /* aiming point */
+ double theta; /* slope in radians */
+ box* bp; /* if not null, points to bbox of
+ * rectangular area that is port target
+ */
+ boolean constrained,defined;
+ unsigned char order; /* for mincross (?) */
+} port_t;
+
+typedef struct Agedgeinfo_s {
+ Agrec_t hdr;
+ /* to assign levels */
+ float minlen;
+ float weight;
+ int tree_index;
+ int cutval;
+
+ /* to run mincross */
+ int xpenalty; /* crossing weight */
+ port_t tailport, headport;
+
+ vpath_t *path;
+
+} Agedgeinfo_t;
+
+#define ED_FIELD(e,field) (((Agedgeinfo_t*)((e)->base.data))->field)
+#define ED_minlen(e) ED_FIELD(e,minlen)
+#define ED_weight(e) ED_FIELD(e,weight)
+#define ED_tree_index(e) ED_FIELD(e,tree_index)
+#define ED_cutval(e) ED_FIELD(e,cutval)
+#define ED_xpenalty(e) ED_FIELD(e,xpenalty)
+#define ED_tailport(e) ED_FIELD(e,tailport)
+#define ED_headport(e) ED_FIELD(e,headport)
+#define ED_orig(e) ED_FIELD(e,orig)
+
+
+/********** variable constants **********/
+#define BACKWARD_PENALTY 1000
+#define STRONG_CLUSTER_WEIGHT 1000
+#define VEDGE_PENALTY 1
+#define CEDGE_PENALTY (1<<30)-1
+#define PORTRANGE 1e+6 /* port coordinates cannot be bigger than this */
+
+ /* xpos phase */
+#define SHORT_FACTOR 1
+#define LONG_FACTOR 2
+#define LONG_END_FACTOR 4
+
+/********** useful symbols **********/
+#define MINRANK 1
+#define SOURCERANK 2
+#define MAXRANK 3
+#define SINKRANK 4
+#define SAMERANK 5
+#define NORANK 6
+
+
+
+/********** libgraph extensions **********/
+int mapbool(char *str, int defval);
+
+#ifndef NIL
+#define NIL(t) ((t)0) /* agrees with CDT */
+#endif
+#define NILgraph NIL(Agraph_t*)
+#define NILnode NIL(Agnode_t*)
+#define NILedge NIL(Agedge_t*)
+
+
+/********** useful macros **********/
+#ifndef MAXINT
+#define MAXINT (int)(~((int)0) ^ (1 << (sizeof(int)*8-1)))
+#endif
+#define MAX(a,b) ((a)>=(b)?(a):(b))
+#define MIN(a,b) ((a)<=(b)?(a):(b))
+#define NEW(t) ((t*)zmalloc(sizeof(t)))
+#define N_NEW(n,t) ((t*)zmalloc((n)*sizeof(t)))
+#define alloc_elist(n,L) do {L.size = 0; L.list = N_NEW(n + 1,edge_t*); } while (0)
+#define free_list(L) do {if (L.list) free(L.list);} while (0)
+#define ALLOC(size,ptr,type) (ptr? (type*)realloc(ptr,(size)*sizeof(type)):(type*)malloc((size)*sizeof(type)))
+
+
+/********** graphviz entry points **********/
+extern int dot_Verbose;
+void rank(graph_t *g, int balance, int maxiter);
+
+Agraph_t *dot_lca(Agraph_t *c0, Agraph_t *c1);
+
+#include "au_.h"
--- /dev/null
+/*
+ * Network Simplex Algorithm for Ranking Nodes of a DAG
+ */
+
+#include "newdot.h"
+
+static int init_graph(graph_t *);
+static void dfs_cutval(node_t* v, edge_t* par);
+static int dfs_range(node_t* v, edge_t* par, int low);
+static int x_val(edge_t* e, node_t* v, int dir);
+
+#define LENGTH(e) (ND_rank(aghead(e)) - ND_rank(agtail(e)))
+#define SLACK(e) (LENGTH(e) - ED_minlen(e))
+#define SEQ(a,b,c) (((a) <= (b)) && ((b) <= (c)))
+#define TREE_EDGE(e) (ED_tree_index(e) >= 0)
+
+static graph_t *G;
+static int N_nodes,N_edges;
+static int Minrank,Maxrank;
+static int S_i; /* search index for enter_edge */
+static int Search_size;
+#define SEARCHSIZE 30
+static elist_t Tree_edge;
+static nlist_t Tree_node;
+
+static void
+add_tree_edge(edge_t* e)
+{
+ node_t *n;
+ if (TREE_EDGE(e)) abort();
+ ED_tree_index(e) = Tree_edge.size;
+ Tree_edge.list[Tree_edge.size++] = e;
+ if (ND_mark(agtail(e)) == FALSE) Tree_node.list[Tree_node.size++] = agtail(e);
+ if (ND_mark(aghead(e)) == FALSE) Tree_node.list[Tree_node.size++] = aghead(e);
+ n = agtail(e);
+ ND_mark(n) = TRUE;
+ ND_tree_out(n).list[ND_tree_out(n).size++] = e;
+ ND_tree_out(n).list[ND_tree_out(n).size] = NULL;
+ n = aghead(e);
+ ND_mark(n) = TRUE;
+ ND_tree_in(n).list[ND_tree_in(n).size++] = e;
+ ND_tree_in(n).list[ND_tree_in(n).size] = NULL;
+}
+
+static void
+exchange_tree_edges(edge_t *e,edge_t *f)
+{
+ int i,j;
+ node_t *n;
+
+ ED_tree_index(f) = ED_tree_index(e);
+ Tree_edge.list[ED_tree_index(e)] = f;
+ ED_tree_index(e) = -1;
+
+ n = agtail(e);
+ i = --(ND_tree_out(n).size);
+ for (j = 0; j <= i; j++) if (ND_tree_out(n).list[j] == e) break;
+ ND_tree_out(n).list[j] = ND_tree_out(n).list[i];
+ ND_tree_out(n).list[i] = NULL;
+ n = aghead(e);
+ i = --(ND_tree_in(n).size);
+ for (j = 0; j <= i; j++) if (ND_tree_in(n).list[j] == e) break;
+ ND_tree_in(n).list[j] = ND_tree_in(n).list[i];
+ ND_tree_in(n).list[i] = NULL;
+
+ n = agtail(f);
+ ND_tree_out(n).list[ND_tree_out(n).size++] = f;
+ ND_tree_out(n).list[ND_tree_out(n).size] = NULL;
+ n = aghead(f);
+ ND_tree_in(n).list[ND_tree_in(n).size++] = f;
+ ND_tree_in(n).list[ND_tree_in(n).size] = NULL;
+}
+
+static
+void init_rank(void)
+{
+ int ctr;
+ queue *Q;
+ node_t *v;
+ edge_t *e;
+
+ Q = new_queue(N_nodes);
+ ctr = 0;
+
+ for (v = agfstnode(G); v; v = agnxtnode(G,v)) {
+ if (ND_priority(v) == 0) enqueue(Q,v);
+ }
+
+ while ((v = dequeue(Q))) {
+ ND_rank(v) = 0;
+ ctr++;
+ for (e = agfstin(G,v); e; e = agnxtin(G,e))
+ ND_rank(v) = MAX(ND_rank(v),ND_rank(agtail(e)) + ED_minlen(e));
+ for (e = agfstout(G,v); e; e = agnxtout(G,e))
+ if (--(ND_priority(aghead(e))) <= 0) enqueue(Q,aghead(e));
+ }
+ if (ctr != N_nodes) {
+ agerr(AGERR, "trouble in init_rank\n");
+ for (v = agfstnode(G); v; v = agnxtnode(G,v)) {
+ if (ND_priority(v))
+ agerr(AGPREV, "\t%s %d\n",agnameof(v),ND_priority(v));
+ }
+ }
+ free_queue(Q);
+}
+
+static node_t *
+incident(edge_t* e)
+{
+ if (ND_mark(agtail(e))) {
+ if (ND_mark(aghead(e)) == FALSE)
+ return agtail(e);
+ }
+ else {
+ if (ND_mark(aghead(e)))
+ return aghead(e);
+ }
+ return NULL;
+}
+
+static edge_t *
+leave_edge (void)
+{
+ edge_t *f,*rv = NULL;
+ int j,cnt = 0;
+
+ j = S_i;
+ while (S_i < Tree_edge.size) {
+ if (ED_cutval((f = Tree_edge.list[S_i])) < 0) {
+ if (rv) {
+ if (ED_cutval(rv) > ED_cutval(f)) rv = f;
+ }
+ else rv = Tree_edge.list[S_i];
+ if (++cnt >= Search_size) return rv;
+ }
+ S_i++;
+ }
+ if (j > 0) {
+ S_i = 0;
+ while (S_i < j) {
+ if (ED_cutval((f = Tree_edge.list[S_i])) < 0) {
+ if (rv) {
+ if (ED_cutval(rv) > ED_cutval(f)) rv = f;
+ }
+ else rv = Tree_edge.list[S_i];
+ if (++cnt >= Search_size) return rv;
+ }
+ S_i++;
+ }
+ }
+ return rv;
+}
+
+static edge_t *Enter;
+static int Low,Lim,Slack;
+
+static void
+dfs_enter_outedge(node_t* v)
+{
+ int i, slack;
+ edge_t *e;
+
+ for (e = agfstout(G,v); e; e = agnxtout(G,e)) {
+ if (TREE_EDGE(e) == FALSE) {
+ if (!SEQ(Low,ND_lim(aghead(e)),Lim)) {
+ slack = SLACK(e);
+ if ((slack < Slack) || (Enter == NULL)) {
+ Enter = e;
+ Slack = slack;
+ }
+ }
+ }
+ else if (ND_lim(aghead(e)) < ND_lim(v)) dfs_enter_outedge(aghead(e));
+ }
+ for (i = 0; (e = ND_tree_in(v).list[i]) && (Slack > 0); i++)
+ if (ND_lim(agtail(e)) < ND_lim(v)) dfs_enter_outedge(agtail(e));
+}
+
+static void
+dfs_enter_inedge(node_t* v)
+{
+ int i,slack;
+ edge_t *e;
+
+ for (e = agfstin(G,v); e; e = agnxtin(G,e)) {
+ if (TREE_EDGE(e) == FALSE) {
+ if (!SEQ(Low,ND_lim(agtail(e)),Lim)) {
+ slack = SLACK(e);
+ if ((slack < Slack) || (Enter == NULL)) {
+ Enter = e;
+ Slack = slack;
+ }
+ }
+ }
+ else if (ND_lim(agtail(e)) < ND_lim(v)) dfs_enter_inedge(agtail(e));
+ }
+ for (i = 0; (e = ND_tree_out(v).list[i]) && (Slack > 0); i++)
+ if (ND_lim(aghead(e)) < ND_lim(v)) dfs_enter_inedge(aghead(e));
+}
+
+static edge_t *
+enter_edge(edge_t* e)
+{
+ node_t *v;
+ int outsearch;
+
+ /* v is the down node */
+ if (ND_lim(agtail(e)) < ND_lim(aghead(e))) {v = agtail(e); outsearch = FALSE;}
+ else {v = aghead(e);outsearch = TRUE;}
+ Enter = NULL;
+ Slack = MAXINT;
+ Low = ND_low(v);
+ Lim = ND_lim(v);
+ if (outsearch) dfs_enter_outedge(v);
+ else dfs_enter_inedge(v);
+ return Enter;
+}
+
+static int
+treesearch(node_t* v)
+{
+ edge_t *e;
+
+ for (e = agfstout(G,v); e; e = agnxtout(G,e)) {
+ if ((ND_mark(aghead(e)) == FALSE) && (SLACK(e) == 0)) {
+ add_tree_edge(e);
+ if ((Tree_edge.size == N_nodes-1) || treesearch(aghead(e))) return TRUE;
+ }
+ }
+ for (e = agfstin(G,v); e; e = agnxtin(G,e)) {
+ if ((ND_mark(agtail(e)) == FALSE) && (SLACK(e) == 0)) {
+ add_tree_edge(e);
+ if ((Tree_edge.size == N_nodes-1) || treesearch(agtail(e))) return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static int
+tight_tree(void)
+{
+ int i;
+ node_t *n;
+
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ ND_mark(n) = FALSE;
+ ND_tree_in(n).list[0] = ND_tree_out(n).list[0] = NULL;
+ ND_tree_in(n).size = ND_tree_out(n).size = 0;
+ }
+ for (i = 0; i < Tree_edge.size; i++) ED_tree_index(Tree_edge.list[i]) = -1;
+
+ Tree_node.size = Tree_edge.size = 0;
+ for (n = agfstnode(G); n && (Tree_edge.size == 0); n = agnxtnode(G,n))
+ treesearch(n);
+ return Tree_node.size;
+}
+
+static void
+init_cutvalues(void)
+{
+ dfs_range(agfstnode(G),NULL,1);
+ dfs_cutval(agfstnode(G),NULL);
+}
+
+static void
+feasible_tree(void)
+{
+ int i,delta;
+ node_t *n;
+ edge_t *e,*f;
+
+ if (N_nodes <= 1) return;
+ while (tight_tree() < N_nodes) {
+ e = NULL;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ for (f = agfstout(G,n); f; f = agnxtout(G,f)) {
+ if ((TREE_EDGE(f) == FALSE) && incident(f) && ((e == NULL)
+ || (SLACK(f) < SLACK(e)))) e = f;
+ }
+ }
+ if (e) {
+ delta = SLACK(e);
+ if (delta) {
+ if (incident(e) == aghead(e)) delta = -delta;
+ for (i = 0; i < Tree_node.size; i++)
+ ND_rank(Tree_node.list[i]) += delta;
+ }
+ }
+ else {
+#ifdef DEBUG
+ fprintf(stderr,"not in tight tree:\n");
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ for (i = 0; i < Tree_node.size; i++)
+ if (Tree_node.list[i] == n) break;
+ if (i >= Tree_node.size) fprintf(stderr,"\t%s\n",agnameof(n));
+ }
+#endif
+ abort();
+ }
+ }
+ init_cutvalues();
+}
+
+/* walk up from v to LCA(v,w), setting new cutvalues. */
+static node_t *
+treeupdate(node_t *v, node_t *w, int cutvalue, int dir)
+{
+ edge_t *e;
+ int d;
+
+ while (!SEQ(ND_low(v),ND_lim(w),ND_lim(v))) {
+ e = ND_par(v);
+ if (v == agtail(e)) d = dir; else d = NOT(dir);
+ if (d) ED_cutval(e) += cutvalue; else ED_cutval(e) -= cutvalue;
+ if (ND_lim(agtail(e)) > ND_lim(aghead(e))) v = agtail(e); else v = aghead(e);
+ }
+ return v;
+}
+
+static void
+rerank(node_t* v, int delta)
+{
+ int i;
+ edge_t *e;
+
+ ND_rank(v) -= delta;
+ for (i = 0; (e = ND_tree_out(v).list[i]); i++)
+ if (e != ND_par(v)) rerank(aghead(e),delta);
+ for (i = 0; (e = ND_tree_in(v).list[i]); i++)
+ if (e != ND_par(v)) rerank(agtail(e),delta);
+}
+
+/* e is the tree edge that is leaving and f is the nontree edge that
+ * is entering. compute new cut values, ranks, and exchange e and f.
+ */
+void
+update(edge_t *e, edge_t *f)
+{
+ int cutvalue,delta;
+ node_t *lca;
+
+ delta = SLACK(f);
+ /* "for (v = in nodes in tail side of e) do ND_rank(v) -= delta;" */
+ if (delta > 0) {
+ int s;
+ s = ND_tree_in(agtail(e)).size + ND_tree_out(agtail(e)).size;
+ if (s == 1) rerank(agtail(e),delta);
+ else {
+ s = ND_tree_in(aghead(e)).size + ND_tree_out(aghead(e)).size;
+ if (s == 1) rerank(aghead(e),-delta);
+ else {
+ if (ND_lim(agtail(e)) < ND_lim(aghead(e))) rerank(agtail(e),delta);
+ else rerank(aghead(e),-delta);
+ }
+ }
+ }
+
+ cutvalue = ED_cutval(e);
+ lca = treeupdate(agtail(f),aghead(f),cutvalue,1);
+ if (treeupdate(aghead(f),agtail(f),cutvalue,0) != lca) abort();
+ ED_cutval(f) = -cutvalue;
+ ED_cutval(e) = 0;
+ exchange_tree_edges(e,f);
+ dfs_range(lca,ND_par(lca),ND_low(lca));
+}
+
+static void
+scan_and_normalize(void)
+{
+ node_t *n;
+
+ Minrank = MAXINT;
+ Maxrank = -MAXINT;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ Minrank = MIN(Minrank,ND_rank(n));
+ Maxrank = MAX(Maxrank,ND_rank(n));
+ }
+ if (Minrank != 0) {
+ for (n = agfstnode(G); n; n = agnxtnode(G,n))
+ ND_rank(n) -= Minrank;
+ Maxrank -= Minrank;
+ Minrank = 0;
+ }
+}
+
+static void
+LR_balance(void)
+{
+ int i,delta;
+ node_t *n;
+ edge_t *e,*f;
+
+ for (i = 0; i < Tree_edge.size; i++) {
+ e = Tree_edge.list[i];
+ if (ED_cutval(e) == 0) {
+ f = enter_edge(e);
+ if (f == NULL) continue;
+ delta = SLACK(f);
+ if (delta <= 1) continue;
+ if (ND_lim(agtail(e)) < ND_lim(aghead(e))) rerank(agtail(e),delta/2);
+ else rerank(aghead(e),-delta/2);
+ }
+ }
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ free_list(ND_tree_in(n));
+ free_list(ND_tree_out(n));
+ ND_mark(n) = FALSE;
+ }
+}
+
+static int
+countable_node(node_t *n)
+{
+ return TRUE; /* could be false for slacknodes */
+}
+
+static void
+TB_balance(void)
+{
+ node_t *n;
+ edge_t *e;
+ int i,low,high,choice,*nrank;
+ int inweight,outweight;
+
+ scan_and_normalize();
+
+ /* find nodes that are not tight and move to less populated ranks */
+ nrank = N_NEW(Maxrank+1,int);
+ for (i = 0; i <= Maxrank; i++) nrank[i] = 0;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n))
+ if (countable_node(n)) nrank[ND_rank(n)]++;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ if (!countable_node(n)) continue;
+ inweight = outweight = 0;
+ low = 0;
+ high = Maxrank;
+ for (e = agfstin(G,n); e; e = agnxtin(G,e)) {
+ inweight += ED_weight(e);
+ low = MAX(low,ND_rank(agtail(e)) + ED_minlen(e));
+ }
+ for (e = agfstout(G,n); e; e = agnxtout(G,e)) {
+ outweight += ED_weight(e);
+ high = MIN(high,ND_rank(aghead(e)) - ED_minlen(e));
+ }
+ if (low < 0) low = 0; /* vnodes can have ranks < 0 */
+ if (inweight == outweight) {
+ choice = low;
+ for (i = low + 1; i <= high; i++)
+ if (nrank[i] < nrank[choice]) choice = i;
+ nrank[ND_rank(n)]--; nrank[choice]++;
+ ND_rank(n) = choice;
+ }
+ free_list(ND_tree_in(n));
+ free_list(ND_tree_out(n));
+ ND_mark(n) = FALSE;
+ }
+ free(nrank);
+}
+
+static int
+init_graph(graph_t* g)
+{
+ int i,feasible;
+ node_t *n;
+ edge_t *e;
+
+ G = g;
+ N_nodes = N_edges = S_i = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ ND_mark(n) = FALSE;
+ N_nodes++;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) N_edges++;
+ }
+
+ Tree_node.list = ALLOC(N_nodes,Tree_node.list,node_t*);
+ Tree_node.size = 0;
+ Tree_edge.list = ALLOC(N_nodes,Tree_edge.list,edge_t*);
+ Tree_edge.size = 0;
+
+ feasible = TRUE;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ ND_priority(n) = 0;
+ i = 0;
+ for (e = agfstin(G,n); e; e = agnxtin(G,e)) {
+ i++;
+ ND_priority(n)++;
+ ED_cutval(e) = 0;
+ ED_tree_index(e) = -1;
+ if (feasible && (ND_rank(aghead(e)) - ND_rank(agtail(e)) < ED_minlen(e)))
+ feasible = FALSE;
+ }
+ ND_tree_in(n).list = N_NEW(i+1,edge_t*);
+ ND_tree_in(n).size = 0;
+ for (e = agfstout(G,n); e; e = agnxtout(G,e)) i++;
+ ND_tree_out(n).list = N_NEW(i+1,edge_t*);
+ ND_tree_out(n).size = 0;
+ }
+ return feasible;
+}
+
+void
+rank(graph_t *g, int balance, int maxiter)
+{
+ int iter = 0,feasible;
+ char *s,*ns = "network simplex: ";
+ edge_t *e,*f;
+
+ if (dot_Verbose) start_timer();
+ feasible = init_graph(g);
+ if (!feasible) init_rank();
+ if (maxiter <= 0) return;
+
+ if ((s = agget(g,"searchsize"))) Search_size = atoi(s);
+ else Search_size = SEARCHSIZE;
+
+ feasible_tree();
+ while ((e = leave_edge())) {
+ f = enter_edge(e);
+ update(e,f);
+ iter++;
+ if (dot_Verbose && (iter % 100 == 0)) {
+ if (iter % 1000 == 100) fputs(ns,stderr);
+ fprintf(stderr,"%d ",iter);
+ if (iter % 1000 == 0) fputc('\n',stderr);
+ }
+ if (iter >= maxiter) break;
+ }
+ switch(balance){
+ case 1: TB_balance(); break;
+ case 2: LR_balance(); break;
+ default: scan_and_normalize(); break;
+ }
+ if (dot_Verbose) {
+ if (iter >= 100) fputc('\n',stderr);
+ fprintf(stderr,"%s%d nodes %d edges %d iter %.2f sec\n",
+ ns,N_nodes,N_edges,iter,elapsed_sec());
+ }
+}
+
+/* set cut value of f, assuming values of edges on one side were already set */
+static void
+x_cutval(edge_t* f)
+{
+ node_t *v;
+ edge_t *e;
+ int sum,dir;
+
+ /* set v to the node on the side of the edge already searched */
+ if (ND_par(agtail(f)) == f) { v = agtail(f); dir = 1; }
+ else { v = aghead(f); dir = -1; }
+
+ sum = 0;
+ for (e = agfstout(G,v); e; e = agnxtout(G,e)) sum += x_val(e,v,dir);
+ for (e = agfstin(G,v); e; e = agnxtin(G,e)) sum += x_val(e,v,dir);
+ ED_cutval(f) = sum;
+}
+
+static int
+x_val(edge_t* e, node_t* v, int dir)
+{
+ node_t *other;
+ int d,rv,f;
+
+ if (agtail(e) == v) other = aghead(e); else other = agtail(e);
+ if (!(SEQ(ND_low(v),ND_lim(other),ND_lim(v)))) {f = 1; rv = ED_weight(e);}
+ else {
+ f = 0;
+ if (TREE_EDGE(e)) rv = ED_cutval(e);
+ else rv = 0;
+ rv -= ED_weight(e);
+ }
+ if (dir > 0) {if (aghead(e) == v) d = 1; else d = -1;}
+ else {if (agtail(e) == v) d = 1; else d = -1; }
+ if (f) d = -d;
+ if (d < 0) rv = -rv;
+ return rv;
+}
+
+static void
+dfs_cutval(node_t* v, edge_t* par)
+{
+ int i;
+ edge_t *e;
+
+ for (i = 0; (e = ND_tree_out(v).list[i]); i++)
+ if (e != par) dfs_cutval(aghead(e),e);
+ for (i = 0; (e = ND_tree_in(v).list[i]); i++)
+ if (e != par) dfs_cutval(agtail(e),e);
+ if (par) x_cutval(par);
+}
+
+static int
+dfs_range(node_t* v, edge_t* par, int low)
+{
+ edge_t *e;
+ int i,lim;
+
+ lim = low;
+ ND_par(v) = par;
+ ND_low(v) = low;
+ for (i = 0; (e = ND_tree_out(v).list[i]); i++)
+ if (e != par) lim = dfs_range(aghead(e),e,lim);
+ for (i = 0; (e = ND_tree_in(v).list[i]); i++)
+ if (e != par) lim = dfs_range(agtail(e),e,lim);
+ ND_lim(v) = lim;
+ return lim + 1;
+}
+
+#ifdef DEBUG
+void tchk(void)
+{
+ int i,n_cnt,e_cnt;
+ node_t *n;
+ edge_t *e;
+
+ n_cnt = 0;
+ e_cnt = 0;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ n_cnt++;
+ for (i = 0; (e = ND_tree_out(n).list[i]); i++) {
+ e_cnt++;
+ if (SLACK(e) > 0)
+ printf("not a tight tree %p",(void*)e);
+ }
+ }
+ if ((n_cnt != Tree_node.size) || (e_cnt != Tree_edge.size))
+ printf("something missing\n");
+}
+
+void check_cutvalues(void)
+{
+ node_t *v;
+ edge_t *e;
+ int i,save;
+
+ for (v = agfstnode(G); v; v = agnxtnode(G,v)) {
+ for (i = 0; (e = ND_tree_out(v).list[i]); i++) {
+ save = ED_cutval(e);
+ x_cutval(e);
+ if (save != ED_cutval(e)) abort();
+ }
+ }
+}
+
+int
+check_ranks(void)
+{
+ int cost = 0;
+ node_t *n;
+ edge_t *e;
+
+ for (n = agfstnode(G); n; n = agnxtnode(G,n)) {
+ for (e = agfstout(G,n); e; e = agnxtout(G,e)) {
+ cost += (ED_weight(e))*abs(LENGTH(e));
+ if (ND_rank(aghead(e)) - ND_rank(agtail(e)) - ED_minlen(e) < 0) abort();
+ }
+ }
+ fprintf(stderr,"rank cost %d\n",cost);
+ return cost;
+}
+
+void checktree(void)
+{
+ int i,n = 0,m = 0;
+ node_t *v;
+ edge_t *e;
+
+ for (v = agfstnode(G); v; v = agnxtnode(G,v)) {
+ for (i = 0; (e = ND_tree_out(v).list[i]); i++) n++;
+ if (i != ND_tree_out(v).size) abort();
+ for (i = 0; (e = ND_tree_in(v).list[i]); i++) m++;
+ if (i != ND_tree_in(v).size) abort();
+ }
+ printf("%d %d %d\n",Tree_edge.size,n,m);
+}
+
+void checkdfs(node_t* n)
+{
+ edge_t *e;
+ node_t *w;
+
+ if (ND_mark(n)) return;
+ ND_mark(n) = TRUE;
+ ND_onstack(n) = TRUE;
+ for (e = agfstout(G,n); e; e = agnxtout(G,e)) {
+ w = aghead(e);
+ if (ND_onstack(w))
+ fprintf(stderr,"cycle involving %s %s\n",agnameof(n),agnameof(w));
+ else {
+ if (ND_mark(w) == FALSE) checkdfs(w);
+ }
+ }
+ ND_onstack(n) = FALSE;
+}
+
+void check_cycles(graph_t* g)
+{
+ node_t *n;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n))
+ ND_mark(n) = ND_onstack(n) = FALSE;
+ for (n = agfstnode(G); n; n = agnxtnode(G,n))
+ checkdfs(n);
+}
+#endif /* DEBUG */
--- /dev/null
+void dot_position(Agraph_t *model)
+{
+ make_variables();
+ node_separation_constraints();
+ cluster_containment_constraints();
+ edge_cost_constraints();
+}
+
+/* g is a model graph */
+static void node_separation_constraints(Agraph_t *g)
+{
+ rank_t *r;
+
+
+ for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
+ r = GD_rank(g)[i];
+ left = NILnode;
+ for (j = leftmost(g,r); j <= rightmost(g,r); j++) {
+ right = r->v[j];
+ node_sep(left,right);
+ right = left;
+ }
+ }
+}
+
+/*
+ * this function has to traverse the subgraph hierarchy from the
+ * user graph because we flattened out the model subgraphs.
+ */
+static void cluster_containment_constraints(Agraph_t *upar, Agraph_t *ug)
+{
+ Agraph_t *container;
+
+ if (is_a_cluster(upar) && is_a_cluster(ug))
+ cluster_contain(upar,ug);
+ container = is_a_cluster(ug)? ug : upar;
+ for (subg = agfstsub(ug); subg; subg = agnxtsubg(ug,subg))
+ cluster_containment_constraints(container,subg);
+}
+
+static void cluster_contain(Agraph_t *u_outer, Agraph_t *u_inner)
+{
+ Agraph_t *Xg;
+ int sep;
+ Agnode_t *outer_left, *outer_right, *inner_left, *inner_right;
+
+ sep = user_cluster_sep(u_inner);
+ Xg = constraint_graph(u_outer);
+ outer_left = leftvar(GD_model(u_outer));
+ outer_right = rightvar(GD_model(u_outer));
+ inner_left = leftvar(GD_model(u_inner));
+ inner_right = rightvar(GD_model(u_inner));
+ clust_sep_edge(outer_left,inner_left);
+ clust_sep_edge(inner_right,outer_right);
+}
+
+static void clust_sep_edge(Agraph_t *Xg, Agnode_t *left, Agnode_t *right, int sep)
+{
+ Agedge_t *e;
+
+ e = agedge(Xg, left, right);
+ ED_weight(e) = 0;
+ ED_minlen(e) = sep;
+}
+
+static void edge_cost_constraints()
+{
+}
--- /dev/null
+#include "newdot.h"
+
+/*
+ * a queue of nodes
+ */
+queue *
+new_queue(int sz)
+{
+ queue *q = NEW(queue);
+
+ if (sz <= 1) sz = 2;
+ q->head = q->tail = q->store = N_NEW(sz,node_t*);
+ q->limit = q->store + sz;
+ return q;
+}
+
+void
+free_queue(queue* q)
+{
+ free(q->store);
+ free(q);
+}
+
+void
+enqueue(queue* q, node_t* n)
+{
+ *(q->tail++) = n;
+ if (q->tail >= q->limit) q->tail = q->store;
+}
+
+node_t *
+dequeue(queue* q)
+{
+ node_t *n;
+ if (q->head == q->tail) n = NULL;
+ else {
+ n = *(q->head++);
+ if (q->head >= q->limit) q->head = q->store;
+ }
+ return n;
+}
--- /dev/null
+/*
+From: Andre Reinald <Andre.Reinald@vtcom.fr>
+To: submissive@cubic.org
+
+I though you might be intersted by some modifications I brought to your
+code in order to improve legibility and slightly speed.
+
+Well, my source is commented and I often wrote 4 lines where you had
+only one, but the generated code is smaller and it gives more
+opportunity to check intermediate steps while debugging.
+
+By the way, I did so on a PowerMac with CodeWarrior compiler, so your
+source is really portable.
+*/
+
+/* hacked by north to support a data pointer with each record */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "radix.h"
+
+/* complicated expression better fits as macro (or inline in C++) */
+#define ByteOf(x) (((x) >> bitsOffset) & 0xff)
+
+typedef unsigned long ulong;
+typedef radixrec_t rec;
+
+/* replaced byte with bitsOffset to avoid *8 operation in loop */
+static void radix (short bitsOffset, ulong N, rec *source, rec *dest)
+{
+/* suppressed the need for index as it is reported in count */
+ ulong count[256];
+/* added temp variables to simplify writing, understanding and compiler optimization job */
+/* most of them will be allocated as registers */
+ ulong *cp, c, i, s;
+ rec *sp, srec;
+
+/* faster than MemSet */
+ cp = count;
+ for (i = 256; i > 0; --i, ++cp)
+ *cp = 0;
+
+/* count occurences of every byte value */
+ sp = source;
+ for (i = N; i > 0; --i, ++sp) {
+ cp = count + ByteOf (sp->key);
+ ++(*cp);
+ }
+
+/* transform count into index by summing elements and storing into same array */
+ s = 0;
+ cp = count;
+ for (i = 256; i > 0; --i, ++cp) {
+ c = *cp;
+ *cp = s;
+ s += c;
+ }
+
+/* fill dest with the right values in the right place */
+ sp = source;
+ for (i = N; i > 0; --i, ++sp) {
+ srec = *sp;
+ cp = count + ByteOf (srec.key);
+ dest[*cp] = srec;
+ ++(*cp);
+ }
+}
+
+void radix_sort (rec *source, ulong N)
+{
+/* allocate heap memory to avoid the need of additional parameter */
+ rec *temp = malloc (N * sizeof (rec));
+ assert (temp != NULL);
+
+ radix (0, N, source, temp);
+ radix (8, N, temp, source);
+ radix (16, N, source, temp);
+ radix (24, N, temp, source);
+
+ free (temp);
+}
+
+#ifdef MAIN
+static void make_random (rec *data, ulong N)
+{
+ for ( ; N > 0; --N, ++data) {
+ data->key = rand () | (rand () << 16);
+ data->data = (void*)((data->key)^0xdeadbeef);
+ }
+}
+
+static void check_order (rec *data, ulong N)
+{
+/* only signal errors if any (should not be) */
+ --N;
+ for ( ; N > 0; --N, ++data) {
+ assert (data[0].key <= data[1].key);
+ assert (data->data == (void*)((data->key)^0xdeadbeef));
+ }
+}
+
+/* test for big number of elements */
+static void test_radix (ulong N)
+{
+ rec *data = malloc (N * sizeof (rec));
+ assert (data != NULL);
+
+ make_random (data, N);
+ radix_sort (data, N);
+ check_order (data, N);
+
+ free (data);
+}
+
+void main (void)
+{
+ test_radix (10000000);
+}
+#endif
--- /dev/null
+typedef struct radixrec_s {
+ unsigned long key;
+ void *data;
+} radixrec_t;
+
+void radix_sort(radixrec_t *source, unsigned long N);
--- /dev/null
+#include <stdlib.h>
+#include <assert.h>
+
+// complicated expression better fits as macro (or inline in C++)
+#define ByteOf(x) (*(uchar *) (x))
+
+typedef unsigned long ulong;
+typedef unsigned char uchar;
+
+// replaced byte with bitsOffset to avoid *8 operation in loop
+static void radix (short byteOffset, ulong N, ulong *source, ulong *dest)
+{
+// suppressed the need for index as it is reported in count
+ ulong count[256];
+// added temp variables to simplify writing, understanding and compiler optimization job
+// most of them will be allocated as registers
+ ulong *sp, *cp, s, c, i;
+ uchar *bp;
+
+// faster than MemSet
+ cp = count;
+ for (i = 256; i > 0; --i, ++cp)
+ *cp = 0;
+
+// count occurences of every byte value
+ bp = ((uchar *) source) + byteOffset;
+ for (i = N; i > 0; --i, bp += 4) {
+ cp = count + *bp;
+ ++(*cp);
+ }
+
+// transform count into index by summing elements and storing into same array
+ s = 0;
+ cp = count;
+ for (i = 256; i > 0; --i, ++cp) {
+ c = *cp;
+ *cp = s;
+ s += c;
+ }
+
+// fill dest with the right values in the right place
+ bp = ((uchar *) source) + byteOffset;
+ sp = source;
+ for (i = N; i > 0; --i, bp += 4, ++sp) {
+ cp = count + *bp;
+ dest[*cp] = *sp;
+ ++(*cp);
+ }
+}
+
+static void radix_sort (ulong *source, ulong N)
+{
+// allocate heap memory to avoid the need of additional parameter
+ ulong *temp = (ulong *) malloc (N * sizeof (ulong));
+ assert (temp != NULL);
+
+ radix (3, N, source, temp);
+ radix (2, N, temp, source);
+ radix (1, N, source, temp);
+ radix (0, N, temp, source);
+
+ free (temp);
+}
+
+static void make_random (ulong *data, ulong N)
+{
+ for ( ; N > 0; --N, ++data)
+ *data = rand () | (rand () << 16);
+}
+
+static void check_order (ulong *data, ulong N)
+{
+// only signal errors if any (should not be)
+ --N;
+ for ( ; N > 0; --N, ++data)
+ assert (data[0] <= data[1]);
+}
+
+// test for big number of elements
+static void test_radix (ulong N)
+{
+ ulong *data = (ulong *) malloc (N * sizeof (ulong));
+ assert (data != NULL);
+
+ make_random (data, N);
+ radix_sort (data, N);
+ check_order (data, N);
+
+ free (data);
+}
+
+int main (int argc, const char ** argv )
+{
+ test_radix (5000000);
+}
--- /dev/null
+/*
+ http://www.cubic.org/~submissive/sourcerer/radix.htm
+ http://www.cubic.org/~submissive/sourcerer/download/radix_ar_2001.c
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+
+// complicated expression better fits as macro (or inline in C++)
+#define ByteOf(x) (*(uchar *) (x))
+
+typedef unsigned long ulong;
+typedef unsigned char uchar;
+
+typedef struct rec_s {
+ ulong key;
+} rec;
+
+// replaced byte with bitsOffset to avoid *8 operation in loop
+static void radix (short byteOffset, ulong N, rec *source, rec *dest)
+{
+// suppressed the need for index as it is reported in count
+ ulong count[256];
+// added temp variables to simplify writing, understanding and compiler optimization job
+// most of them will be allocated as registers
+ ulong *cp, s, c, i;
+ uchar *bp;
+ rec *sp;
+
+// faster than MemSet
+ cp = count;
+ for (i = 256; i > 0; --i, ++cp)
+ *cp = 0;
+
+// count occurences of every byte value
+ bp = ((uchar *) source) + byteOffset;
+ for (i = N; i > 0; --i, bp += sizeof(rec)) {
+ cp = count + *bp;
+ ++(*cp);
+ }
+
+// transform count into index by summing elements and storing into same array
+ s = 0;
+ cp = count;
+ for (i = 256; i > 0; --i, ++cp) {
+ c = *cp;
+ *cp = s;
+ s += c;
+ }
+
+// fill dest with the right values in the right place
+ bp = ((uchar *) source) + byteOffset;
+ sp = source;
+ for (i = N; i > 0; --i, bp += sizeof(rec), ++sp) {
+ cp = count + *bp;
+ dest[*cp] = *sp;
+ ++(*cp);
+ }
+}
+
+static void radix_sort (rec *source, ulong N)
+{
+// allocate heap memory to avoid the need of additional parameter
+ rec *temp = (rec *) malloc (N * sizeof (rec));
+ assert (temp != NULL);
+
+ radix (3, N, source, temp);
+ radix (2, N, temp, source);
+ radix (1, N, source, temp);
+ radix (0, N, temp, source);
+
+ free (temp);
+}
+
+static void make_random (rec *data, ulong N)
+{
+ for ( ; N > 0; --N, ++data)
+ data->key = rand () | (rand () << 16);
+}
+
+static void check_order (rec *data, ulong N)
+{
+// only signal errors if any (should not be)
+ --N;
+ for ( ; N > 0; --N, ++data)
+ assert (data[0].key <= data[1].key);
+}
+
+// test for big number of elements
+static void test_radix (ulong N)
+{
+ rec *data = (rec *) malloc (N * sizeof (rec));
+ assert (data != NULL);
+
+ make_random (data, N);
+ radix_sort (data, N);
+ check_order (data, N);
+
+ free (data);
+}
+
+int main (int argc, const char ** argv)
+{
+ test_radix (5000000);
+}
--- /dev/null
+#include "newdot.h"
+
+/* given:
+ * ND_rank(v) int integer level assignments
+ * ND_ranksize(v) int number of levels (>= 1)
+ * ED_xpenalty(e) float crossing penalty factor
+ * find:
+ * ND_pos(v) coord
+ * ED_pos(e) coord*
+ *
+ * todo: graph, edge labels, flat edges
+ */
+
+/* internal graph variables:
+ * user graph
+ * ND_cluster(v) graph* lowest containing cluster
+ * GD_model(g) graph* model graph
+ * GD_parent(g) graph* parent cluster
+ * GD_level int distance from layout root
+ *
+ * model graph objects
+ * GD_usergraph(g) graph* original graph or one of its clusters
+ * GD_minrank(g) int lowest rank index incl. external edges
+ * GD_maxrank(g) int highest rank index incl. external edges
+ * GD_minlocalrank(g) int lowest rank index of internal nodes
+ * GD_maxlocalrank(g) int highest rank index of internal nodes
+ * GD_rank(g) rank_t[]
+ * GD_repdict(g) Dict_t* local representatives of user objects
+ * ND_component(v) int connected component number
+ * ND_order(v) int order within rank
+ * ND_cluster(v) graph* for EXTNODE, its (lowest) user cluster
+ */
+
+/* TODO
+ where do we use port.defined?
+ */
+
+/*** basic user<->model association maps ***/
+/* determine canonical order of n0, n1 */
+static void getlowhigh(Agnode_t **n0, Agnode_t **n1)
+{
+ Agnode_t *temp;
+ int d;
+ d = ND_rank(*n0) - ND_rank(*n1);
+ if ((d < 0) || ((d == 0) && ((*n0)->id > (*n1)->id)))
+ {temp = *n0; *n0 = *n1; *n1 = temp;}
+}
+
+static int repkeycmp(Dt_t *d, void *arg0, void *arg1, Dtdisc_t *disc)
+{
+ void *key0 = ((repkey_t*)arg0)->key;
+ void *key1 = ((repkey_t*)arg1)->key;
+ int rv;
+ switch(agobjkind(key0)) {
+ case AGNODE:
+ rv = ((Agnode_t*)key0)->id - ((Agnode_t*)key1)->id;
+ break;
+ case AGEDGE:
+ rv = ((Agedge_t*)key0)->id - ((Agedge_t*)key1)->id;
+ break;
+ case AGRAPH:
+ /* in libgraph we don't seem to have graph ids, so use pointer */
+ rv = (unsigned long)key0 - (unsigned long) key1;
+ break;
+ default:
+ rv = 0;
+ }
+ return rv;
+}
+
+static Dtdisc_t Repdisc = {
+ 0, /* pass whole object as key */
+ 0, /* key size and type */
+ -1, /* link offset */
+ (Dtmake_f)0,
+ (Dtfree_f)0,
+ (Dtcompar_f) repkeycmp,
+ (Dthash_f)0,
+ (Dtmemory_f)0,
+ (Dtevent_f)0
+};
+
+static Dict_t *repdict(Agraph_t *model)
+{
+ Dict_t *dict;
+
+ dict = GD_repdict(model);
+ if (!dict) dict = GD_repdict(model) = dtopen(&Repdisc,Dttree);
+ return dict;
+}
+
+static rep_t association(Agraph_t *model, void *obj)
+{
+ Dict_t *dict;
+ repkey_t key, rv, *p;
+
+ dict = repdict(model);
+ key.key = obj; /* i hate when other people code like this */
+ if ((p = dtsearch(dict,&key))) rv = *p;
+ else {rv.val.type = 0; rv.val.p = 0;}
+ return rv.val;
+}
+
+static void associate(Agraph_t *model, void *key, rep_t val)
+{
+ Dict_t *dict;
+ repkey_t *newelt;
+
+ assert(association(model,key).type == 0);
+ dict = repdict(model);
+ newelt = NEW(repkey_t);
+ newelt->key = key;
+ newelt->val = val;
+ dtinsert(dict,newelt);
+}
+
+typedef struct component_s {
+ int n;
+ node_t **root;
+ int r;
+} component_t;
+
+
+
+/* from level.c - eventually clean this up. */
+static int is_a_cluster(Agraph_t *g)
+{
+ return ((g == g->root) || (!strncasecmp(g->name,"cluster",7)));
+}
+
+/* find the cluster of n that is an immediate child of g */
+static Agraph_t *subclustof(Agnode_t *n, Agraph_t *g)
+{
+ Agraph_t *rv;
+ for (rv = ND_cluster(n); rv && (GD_parent(rv) != g); rv = GD_parent(rv));
+ return rv;
+}
+
+static void *T_array(int low, int high, int size)
+{
+ char *rv;
+
+ rv = calloc((high - low + 1),size);
+ rv = rv - (low * size);
+ return rv;
+}
+
+static Agnode_t **nodearray(int low, int high)
+{ return (Agnode_t**) T_array(low,high,sizeof(Agnode_t*)); }
+
+static Agedge_t **edgearray(int low, int high)
+{ return (Agedge_t**) T_array(low,high,sizeof(Agedge_t*)); }
+
+static int *intarray(int low, int high)
+{ return (int*) T_array(low,high,sizeof(int)); }
+
+/* internal functions for model graph construction and maintenance */
+static vpath_t *newpath(Agraph_t *model, Agnode_t *u, int low, Agnode_t *v, int high)
+{
+ vpath_t *path;
+ int i;
+ path = NEW(vpath_t);
+ path->v = nodearray(low, high);
+ path->e = edgearray(low, high);
+ for (i = low; i <= high; i++) {
+ if ((i == low) && u) path->v[i] = u;
+ else if ((i == high) && v) path->v[i] = v;
+ else path->v[i] = agnode(model,0);
+ if (i > low) path->e[i-1] = agedge(model,path->v[i-1],path->v[i]);
+ }
+ return path;
+}
+
+/* a given edge can have several other edges (forward or backward)
+ between the same endpoints. here, we choose one of these to be
+ the canonical representative of those edges. */
+static Agedge_t* canonical_edge(Agedge_t* e)
+{
+ Agraph_t *g;
+ Agedge_t *canon;
+
+ g = e->tail->graph;
+ if (ND_rank(e->head) > ND_rank(e->tail))
+ canon = agfindedge(g,e->tail,e->head);
+ else {
+ if
+ }
+}
+
+static void model_edge(Agraph_t *model, Agedge_t *orig)
+{
+ Agedge_t *e;
+ Agnode_t *low, *high, *u, *v;
+ port_t lowport, highport;
+ vpath_t *path;
+ rep_t rep;
+ int i;
+
+ rep = association(model,orig);
+ if (rep.type == 0) {
+ low = orig->tail; high = orig->head;
+ getlowhigh(&low,&high);
+ u = association(model,low).p; assert(u);
+ v = association(model,high).p; assert(v);
+ path = newpath(model,u,ND_rank(low),v,ND_rank(high));
+ rep.type = PATH;
+ rep.p = path;
+ associate(model,orig,rep);
+ }
+ else path = rep.p;
+
+ /* merge the attributes of orig */
+ for (i = path->low; i < path->high; i++) {
+ e = path->e[i];
+ ED_xpenalty(e) += ED_xpenalty(orig);
+ ED_weight(e) += ED_weight(orig);
+ }
+
+ /* deal with ports. note that ends could be swapped! */
+ if (ND_rank(orig->tail) <= ND_rank(orig->head)) {
+ lowport = ED_tailport(orig);
+ highport = ED_headport(orig);
+ }
+ else {
+ highport = ED_tailport(orig);
+ lowport = ED_headport(orig);
+ }
+ if (lowport.defined)
+ path->avgtailport = ((path->weight * path->avgtailport) + ED_weight(orig) * lowport.p.x) / (path->weight + ED_weight(orig));
+ if (highport.defined)
+ path->avgheadport = ((path->weight * path->avgheadport) + ED_weight(orig) * highport.p.x) / (path->weight + ED_weight(orig));
+ path->weight += ED_weight(orig);
+}
+
+/* bind/construct representative of an internal node in a model graph */
+static rep_t model_intnode(Agraph_t *model, Agnode_t *orig)
+{
+ int nr;
+ rep_t rep;
+ Agnode_t *v;
+
+ rep = association(model,orig);
+ if (rep.type) return rep;
+
+ nr = ND_ranksize(orig);
+ if (nr <= 1) { /* simple case */
+ v = rep.p = agnode(model,orig->name);
+ rep.type = NODE;
+ ND_rank(v) = ND_rank(orig);
+ }
+ else { /* multi-rank node case */
+ rep.p = newpath(model,NILnode,ND_rank(orig),NILnode,ND_rank(orig)+nr-1);
+ rep.type = TALLNODE;
+ }
+ associate(model,orig,rep);
+ return rep;
+}
+
+/* bind/construct representative of an external endpoint to a model graph */
+static rep_t model_extnode(Agraph_t *model, Agnode_t *orig)
+{
+ Agnode_t *v;
+ rep_t rep;
+
+ rep = association(model,orig);
+ if (rep.type) return rep;
+
+ /* assume endpoint is represented by one node, even if orig is multi-rank */
+ rep.p = v = agnode(model,orig->name);
+ rep.type = EXTNODE;
+ ND_rank(v) = ND_rank(orig); /* should be ND_rank(orig)+ranksize(orig)? */
+ associate(model,orig,rep);
+ return rep;
+}
+
+/* bind/construct representative of an internal cluster of a model graph */
+static rep_t model_clust(Agraph_t *model, Agraph_t *origclust)
+{
+ rep_t rep;
+
+ rep = association(model,origclust);
+ if (rep.type) return rep;
+
+ rep.p = newpath(model,NILnode,GD_minrank(origclust),NILnode,GD_maxrank(origclust));
+ rep.type = SKELETON;
+ associate(model,origclust,rep);
+ return rep;
+}
+
+#ifdef NOTDEF
+ /* i think we ditched this because we assume nodes are always
+ built before edges */
+/* helper functions for model_edge */
+static rep_t bindnode(Agraph_t *model, Agnode_t *orignode)
+{
+ rep_t rep;
+ if (agcontains(GD_originalgraph(model),orignode))
+ rep = model_intnode(model,orignode);
+ else
+ rep = model_extnode(model,orignode);
+ return rep;
+}
+#endif
+
+static int leftmost(Agraph_t *model, int r) { return 0; }
+static int rightmost(Agraph_t *model, int r) {return GD_rank(model)[r].n - 1;}
+
+static void flat_edges(Agraph_t *clust)
+{
+#ifdef NOTDEF
+ for (n = agfstnode(clust); n; n = agnxtnode(clust)) {
+ for (e = agfstedge(root,n); e; e = agnxtedge(root,e,n)) {
+ }
+ }
+ ordered_edges();
+#endif
+}
+
+static void search_component(Agraph_t *g, Agnode_t *n, int c)
+{
+ Agedge_t *e;
+ ND_component(n) = c;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (ND_component(e->head) < 0)
+ search_component(g,e->head,c);
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (ND_component(e->tail) < 0)
+ search_component(g,e->tail,c);
+}
+
+static int ND_comp_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_component(*(Agnode_t**)arg0) - ND_component(*(Agnode_t**)arg1);
+}
+
+static component_t build_components(Agraph_t *g, boolean down)
+{
+ component_t rv;
+ node_t *n;
+ int r, rootcnt, compcnt, deg;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_component(n) = -1; /* initialize to unknown component */
+
+ compcnt = 0; rootcnt = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ /* set priority for subsequent BFS to install nodes, and record roots */
+ if (down) deg = ND_indeg(n);
+ else deg = ND_outdeg(n);
+ ND_priority(n) = deg;
+ if (deg == 0) rootcnt++;
+ /* count and mark components */
+ if (ND_component(n) < 0) search_component(g,n,compcnt++);
+ }
+
+ rv.n = compcnt;
+ rv.r = rootcnt;
+ rv.root = N_NEW(rv.r,Agnode_t*);
+ r = 0;
+ /* install roots in root list */
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ if (ND_priority(n) == 0) rv.root[r++] = n;
+ /* sort root list so components are contiguous */
+ qsort(rv.root,rv.n,sizeof(node_t*),ND_comp_cmpf);
+ return rv;
+}
+
+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++;
+}
+
+/*
+ populates rank lists of g. there are some key details:
+ 1) the input graph ordering must be respected (in left to right initialization)
+ 2) connected components are separated and marked with indices
+ 3) series-parallel graphs (includes trees, obviously) must not have crossings
+*/
+static void build_ranks(Agraph_t *g, boolean down)
+{
+ queue *q;
+ component_t c;
+ int r;
+ Agnode_t *n;
+ Agedge_t *e;
+
+ c = build_components(g, down);
+
+ /* process each each component */
+ q = new_queue(agnnodes(g)+1);
+ for (r = 0; r < c.r; r++) {
+ enqueue(q,c.root[r]);
+ if ((r + 1 >= c.r)||(ND_component(c.root[r])!=ND_component(c.root[r+1]))) {
+ while ((n = dequeue(q))) {
+ install(g,n);
+ if (down) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (--ND_priority(e->head) == 0) enqueue(q,e->head);
+ }
+ else {
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (--ND_priority(e->tail) == 0) enqueue(q,e->head);
+ }
+ }
+ }
+ }
+ free_queue(q);
+}
+
+/* this predicate indicates if mincross should be run on this cluster */
+static boolean run(Agraph_t *mg)
+{
+ if (GD_pass(mg) > GD_maxpass(mg)) return FALSE;
+ if (GD_pass(mg) - GD_lastwin(mg) > GD_mintry(mg)) return FALSE;
+ GD_pass(mg) = GD_pass(mg) + 1;
+ return TRUE;
+}
+
+static int presort_cmpf(const void *arg0, const void *arg1)
+{
+ Agnode_t *n0, *n1;
+ Agraph_t *c0, *c1;
+
+ n0 = *(Agnode_t**)arg0;
+ n1 = *(Agnode_t**)arg1;
+ c0 = ND_cluster(n0);
+ c1 = ND_cluster(n1);
+ if (c0 == c1) return 0;
+ assert(ND_rank(n0) == ND_rank(n1));
+ n0 = GD_skel(c0)->v[ND_rank(n0)];
+ n1 = GD_skel(c1)->v[ND_rank(n1)];
+ return ND_order(n0) - ND_order(n1);
+}
+
+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;
+ }
+}
+
+/* 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;
+ }
+ }
+}
+
+static void mincross_clust(Agraph_t *ug)
+{
+ Agraph_t *g;
+ g = GD_model(ug);
+ if (run(g)) {
+ presort(ug); /* move the external nodes */
+ subclustports(ug);
+ do {
+ mincross_sweep(g,GD_pass(g)%2,GD_pass(g)%4<2);
+ } while (run(g));
+ transpose_sweep(g,TRUE);
+ restorebest(g);
+ }
+}
+
+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));
+}
+
+/* this assumes one level per node - no mega-nodes */
+static void apply_model(Agraph_t *ug)
+{
+ Agnode_t *un;
+ for (un = agfstnode(ug); un; un = agnxtnode(ug,un))
+ ND_order(un) = ND_order(ND_rep(un));
+}
+
+/* this is a first cut at a top-level planner. it's lame. */
+static void rec_cluster_run(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) mincross_clust(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_run(subg);
+ if (is_a_cluster(ug)) mincross_clust(ug);
+}
+
+/* this is the top level mincross entry point */
+void dot_mincross(Agraph_t *user)
+{
+ rec_cluster_init(user);
+ rec_cluster_run(user);
+ globalopt(user);
+ apply_model(user);
+}
+
+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;
+}
+
+/* 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);
+}
+
+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;
+}
+
+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 mincross_sweep(Agraph_t* g, int dir, boolean reverse)
+{
+ int r,other,low,high,first,last;
+ int hasfixed;
+
+ low = GD_minrank(g);
+ high = GD_maxrank(g);
+ if (dir == 0) return;
+ if (dir > 0) { first = low + 1; last = high; dir = 1;} /* down */
+ else { first = high - 1; last = low; dir = -1;} /* up */
+
+ for (r = first; r != last + dir; r += dir) {
+ other = r - dir;
+ hasfixed = medians(g,r,other);
+ reorder(g,r,reverse,hasfixed);
+ }
+ transpose_sweep(g,NOT(reverse));
+ savebest(g);
+}
+
+
+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;
+}
+
+static void build_flat_graphs(Agraph_t *g)
+{
+}
+
+static int out_cross(node_t *v, node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0,t;
+
+ for (e2 = agfstout(w->graph,w); e2; e2 = agnxtout(w->graph,e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(e2->head);
+ for (e1 = agfstout(v->graph,v); e1; e1 = agnxtout(v->graph,e1)) {
+ t = ND_order(e1->head) - inv;
+ if ((t > 0) || ((t == 0) && (ED_headport(e1).p.x > ED_headport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int in_cross(node_t *v,node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0, t;
+
+ for (e2 = agfstin(w->graph,w); e2; e2 = agnxtin(w->graph,e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(e2->tail);
+ for (e1 = agfstin(v->graph,v); e1; e1 = agnxtin(v->graph,e1)) {
+ t = ND_order(e1->tail) - inv;
+ if ((t > 0) || ((t == 0) && (ED_tailport(e1).p.x > ED_tailport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int int_cmpf(const void *arg0, const void *arg1)
+{
+ return *(int*)arg0 - *(int*)arg1;
+}
+
+
+/* 8 is the number of bits in port.order, an unsigned char */
+#define VAL(node,port) (((node)->u.order << 8) + (port).order)
+
+/*
+ * 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 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 savebest(graph_t *g)
+{
+ int nc;
+ Agnode_t *n;
+
+ nc = crossings(g);
+ if (nc < GD_bestcrossings(g)) {
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_saveorder(n) = ND_order(n);
+ GD_bestcrossings(g) = nc;
+ GD_lastwin(g) = GD_pass(g);
+ }
+}
+
+static int ND_order_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_order(*(Agnode_t**)arg0) - ND_order(*(Agnode_t**)arg1);
+}
+
+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 int crossings(graph_t *g)
+{
+ int i, rv;
+
+ rv = 0;
+ for (i = GD_minrank(g); i < GD_maxrank(g); i++) {
+ rv += crossings_below(g,i);
+ }
+ return rv;
+}
+
+/* build the global (flat) graph of the universe. this is 'flat'
+in the sense that there is one data structure for the entire graph
+(not 'flat' in the sense of flat edges within the same level.)
+*/
+static rank_t *globalize(Agraph_t *user, Agraph_t *topmodel)
+{
+ rank_t *globrank;
+ int minr,maxr,r;
+
+ /* setup bookkeeping */
+ interclusterpaths(user, topmodel);
+
+ /* allocate global ranks */
+ minr = GD_minrank(topmodel);
+ maxr = GD_maxrank(topmodel);
+ globrank = T_array(minr,maxr,sizeof(rank_t));
+ countup(user,globrank);
+ for (r = minr; r <= maxr; r++) {
+ globrank[r].v = N_NEW(globrank[r].n+1,Agnode_t*); /* NIL at end */
+ globrank[r].n = 0; /* reset it */
+ }
+
+ /* installation */
+ for (r = minr; r <= maxr; r++)
+ installglob(user,topmodel,globrank,r);
+
+ removejunk(user, topmodel);
+ reconnect(user, topmodel);
+
+ /* optimization */
+ return globrank;
+}
+
+static void countup(Agraph_t *g, rank_t *globr)
+{
+ Agnode_t *n;
+ Agedge_t *e;
+ int r0, r1, low, high, i;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ for (i = 0; i < ND_ranksize(n); i++)
+ globr[ND_rank(n)+i].n += 1;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
+ r0 = ND_rank(e->tail);
+ r1 = ND_rank(e->head);
+ low = MIN(r0,r1);
+ high = MAX(r0,r1);
+ for (i = low + 1; i < high; i++)
+ globr[i].n += 1;
+ }
+ }
+}
+
+static void rec_model_subclusts(Agraph_t *model, Agraph_t *user)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(user))
+ (void) model_clust(model,user);
+ /* note there can be non-cluster subgraphs that contain lower clusters */
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_model_subclusts(model,subg);
+}
+
+static void cluster_contents(Agraph_t *ug)
+{
+ Agraph_t *mg;
+ char *name;
+
+ assert(is_a_cluster(ug));
+ assert(GD_model(ug) == 0);
+
+ name = malloc(strlen(ug->name)+10);
+ sprintf(name,"model_%s",ug->name);
+ GD_model(ug) = mg = agopen(name,AGDIGRAPHSTRICT);
+ free(name);
+
+ /* install internal nodes */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ if (ND_cluster(n) == ug) model_intnode(mg,n);
+ }
+
+ /* install cluster skeletons */
+ rec_model_subclusts(mg,ug);
+
+ /* install external edge endpoints */
+ root = GD_layoutroot(ug);
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(root,n); e; e = agnxtout(root,e)) {
+ if (!agcontains(ug,e->tail))
+ model_extnode(mg,e->tail);
+ if (!agcontains(ug,e->head))
+ model_extnode(mg,e->head);
+ }
+ }
+
+ /* install edges */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n))
+ for (e = agfstout(root,n); e; e = agnxtout(root,e))
+ model_edge(mg,e); /* amazing if this is all it takes */
+}
+
+static void cluster_init(Agraph_t *ug)
+{
+ cluster_contents(ug);
+ flat_edges(ug);
+}
+
+static void rec_cluster_init(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) cluster_init(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_init(subg);
+}
--- /dev/null
+#include "newdot.h"
+
+/* given:
+ * ND_level(v) int integer level assignments
+ * ND_something(v) int number of levels (if > 1)
+ * ED_xpenalty(e) float crossing penalty factor
+ * find:
+ * ND_pos(v) coord
+ * ED_pos(e) coord*
+ *
+ * todo: graph, edge labels
+ */
+
+/* computed values:
+ * user graph
+ * ND_cluster(v) graph* lowest containing cluster
+ * GD_model(g) graph* model graph
+ * GD_parent(g) graph* parent cluster
+ *
+ * model graph objects
+ * GD_minrank(g) int
+ * GD_maxrank(g) int
+ * GD_rank(g) rank_t[]
+ * GD_skel(g) vpath_t* path representing g in its parent
+ * ND_component(v) int connected component number
+ */
+
+ * ND_vrep(user node) <-> ND_vrep(model node : REAL)
+ * ND_erep(model node : XNODE) == user edge
+ * ND_erep(model node : VNODE) == user edge
+ * ND_grep(model node : CNODE) == user cluster
+ */
+
+/* TODO
+ where do we use port.defined?
+ */
+
+typedef struct component_s {
+ int n;
+ node_t **root;
+ int r;
+} component_t;
+
+static Agraph_t *subclustof(Agnode_t *n, Agraph_t *clust);
+static void *T_array(int low, int high, int size);
+static Agnode_t **nodearray(int low, int high);
+static int *intarray(int low, int high);
+static void cluster_model(Agraph_t *clust);
+static void flat_edges(Agraph_t *clust);
+static void search_component(Agraph_t *g, Agnode_t *n, int c);
+static int ND_comp_cmpf(const void *arg0, const void *arg1);
+static component_t build_components(Agraph_t *g, boolean down);
+static void install(Agraph_t *g, Agnode_t *n);
+static void build_ranks(Agraph_t *ug, boolean down);
+static void rec_cluster_init(Agraph_t *userclust);
+static boolean run(Agraph_t *g);
+static void mincross_clust(Agraph_t *clust);
+static void globalopt(Agraph_t *ug);
+static void rec_cluster_run(Agraph_t *ug);
+static void exchange(Agraph_t *g, Agnode_t *u, Agnode_t *v);
+static void transpose_sweep(Agraph_t* g, int reverse);
+static void mincross_sweep(Agraph_t* g, int dir, boolean reverse);
+static int left2right(Agraph_t *g, node_t *v, node_t *w);
+static void build_flat_graphs(Agraph_t *g);
+static boolean medians(Agraph_t *g, int r0, int r1);
+static void reorder(Agraph_t *g, int r, boolean reverse, boolean hasfixed);
+static void savebest(Agraph_t *g);
+static void restorebest(Agraph_t *g);
+static int crossings(Agraph_t *g);
+static int in_cross(node_t *v,node_t *w);
+static int out_cross(node_t *v,node_t *w);
+static rank_t *globalize(Agraph_t *user, Agraph_t *model);
+static void installglob(Agraph_t *user, Agraph_t *fromgraph, rank_t *globr, int r);
+static void countup(Agraph_t *g, rank_t *globr);
+
+static Agnode_t *model_getrep(Agraph_t *ug, Agnode_t *n, Agedge_t *e);
+static Agraph_t *universegraph(Agraph_t *ug);
+static vpath_t *model_skel(Agraph_t *uclust, Agraph_t *model);
+static void removejunk(Agraph_t *ug, Agraph_t *topmodel);
+static void reconnect(Agraph_t *ug, Agraph_t *topmodel);
+
+/* from level.c - eventually clean this up. */
+static int is_a_cluster(Agraph_t *g)
+{
+ return ((g == g->root) || (!strncasecmp(g->name,"cluster",7)));
+}
+
+/* find the cluster of n that is an immediate child of g */
+static Agraph_t *subclustof(Agnode_t *n, Agraph_t *g)
+{
+ Agraph_t *rv;
+ for (rv = ND_cluster(n); rv && (GD_parent(rv) != g); rv = GD_parent(rv));
+ return rv;
+}
+
+static void *T_array(int low, int high, int size)
+{
+ char *rv;
+
+ rv = calloc((high - low + 1),size);
+ rv = rv - (low * size);
+ return rv;
+}
+
+static Agnode_t **nodearray(int low, int high)
+{ return (Agnode_t**) T_array(low,high,sizeof(Agnode_t*)); }
+
+static Agedge_t **edgearray(int low, int high)
+{ return (Agedge_t**) T_array(low,high,sizeof(Agedge_t*)); }
+
+static int *intarray(int low, int high)
+{ return (int*) T_array(low,high,sizeof(int)); }
+
+static Agnode_t *model_realnode(Agraph_t *modelgraph, Agnode_t *orig)
+{
+ Agnode_t *rep;
+ /* this is OK because realnode can only be in one model graph/subgraph */
+ rep = agnode(modelgraph,orig->name);
+ ND_rank(rep) = ND_rank(orig);
+ ND_cluster(rep) = modelgraph;
+ ND_vrep(orig) = rep;
+ ND_vrep(rep) = orig;
+ ND_type(rep) = NODETYPE_REAL;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agnode_t *model_extnode(Agraph_t *modelgraph, Agnode_t *orig, Agedge_t *ue)
+{
+ char name[1024];
+ Agnode_t *rep;
+
+ sprintf(name,"ext_%s_%s",modelgraph->name,orig->name);
+ /* can't be anonymous - needs to be repeatable */
+ rep = agnode(modelgraph,name);
+ ND_rank(rep) = ND_rank(orig);
+ ND_cluster(rep) = modelgraph;
+ ND_erep(rep) = ue;
+ ND_type(rep) = NODETYPE_XNODE;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agnode_t *model_vnode(Agraph_t *modelgraph, Agedge_t *uedge, int level)
+{
+ Agnode_t *rep;
+ rep = agnode(modelgraph,0);
+ ND_rank(rep) = level;
+ ND_cluster(rep) = modelgraph;
+ ND_erep(rep) = uedge; /* this may be bogus, what if multiple? */
+ ND_type(rep) = NODETYPE_VNODE;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agnode_t *model_cnode(Agraph_t *modelgraph, Agraph_t *uclust, int level)
+{
+ Agnode_t *rep;
+ rep = agnode(modelgraph,0);
+ ND_rank(rep) = level;
+ ND_cluster(rep) = uclust;
+ ND_grep(rep) = uclust;
+ ND_type(rep) = NODETYPE_CNODE;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agedge_t *model_vedge(Agraph_t *modelgraph, Agnode_t *tx, Agnode_t *hx, Agedge_t *orig)
+{
+ Agedge_t *e;
+
+ e = agedge(modelgraph,tx,hx);
+ ED_xpenalty(e) = VEDGE_PENALTY * ED_xpenalty(orig);
+ ED_weight(e) = ED_weight(orig);
+ return e;
+}
+
+static Agedge_t *model_cedge(Agraph_t *modelgraph, Agnode_t *tx, Agnode_t *hx)
+{
+ Agedge_t *e;
+
+ e = agedge(modelgraph,tx,hx);
+ ED_xpenalty(e) = CEDGE_PENALTY;
+ ED_weight(e) = 1;
+ return e;
+}
+
+static void getlowhigh(Agnode_t **n0, Agnode_t **n1)
+{
+ Agnode_t *temp;
+ if (ND_rank(*n0) > ND_rank(*n1))
+ {temp = *n0; *n0 = *n1; *n1 = temp;}
+}
+
+vpath_t *newpath(Agnode_t *u, Agnode_t *v)
+{
+ Agnode_t *low = u, *high = v;
+ vpath_t *p;
+
+ getlowhigh(&low,&high);
+ p = NEW(vpath_t);
+ p->key.tail = low;
+ p->key.head = high;
+ p->low = ND_rank(low);
+ p->high = ND_rank(high);
+ p->v = nodearray(p->low, p->high);
+ p->v[p->low] = low;
+ p->v[p->high] = high;
+ p->e = edgearray(p->low, p->high);
+ return p;
+}
+
+static int pathcmp(Dt_t *d, void *arg0, void *arg1, Dtdisc_t *disc)
+{
+ int rv;
+ if ((rv = ((vpath_t*)arg0)->key.tail - ((vpath_t*)arg1)->key.tail) == 0)
+ rv = ((vpath_t*)arg0)->key.head - ((vpath_t*)arg1)->key.head;
+ return rv;
+}
+
+static Dtdisc_t Vpathdisc = {
+ 0, /* pass whole object as key */
+ 0, /* key size and type */
+ -1, /* link offset */
+ (Dtmake_f)0,
+ (Dtfree_f)0,
+ (Dtcompar_f) pathcmp,
+ (Dthash_f)0,
+ (Dtmemory_f)0,
+ (Dtevent_f)0
+};
+
+static Dict_t *pathdict(Agraph_t *model)
+{
+ Dict_t *dict;
+
+ dict = GD_pathdict(model);
+ if (!dict) dict = GD_pathdict(model) = dtopen(&Vpathdisc,Dttree);
+ return dict;
+}
+
+static void pathinsert(Agraph_t *model, vpath_t *p)
+{
+ Dict_t *dict;
+ dict = pathdict(model);
+ p = dtinsert(dict,p);
+}
+
+static vpath_t *pathsearch(Agraph_t *modelgraph, Agnode_t *origlow, Agnode_t *orighigh)
+{
+ Dict_t *dict;
+ vpath_t path, *p;
+
+ dict = pathdict(modelgraph);
+ path.key.tail = origlow;
+ path.key.head = orighigh;
+ p = dtsearch(dict,&path);
+ return p;
+
+}
+
+vpath_t *model_path(Agraph_t *modelgraph, Agnode_t *tv, Agnode_t *hv, Agedge_t *orig)
+{
+ vpath_t *p;
+ Agnode_t *origlow, *orighigh;
+ double old, new, tot, hx, tx;
+ port tp, hp;
+ int i;
+
+ getlowhigh(&tv,&hv);
+ origlow = orig->tail; orighigh = orig->head;
+ getlowhigh(&origlow,&orighigh);
+
+ p = pathsearch(modelgraph,origlow,orighigh);
+ if (!p) {
+ p = newpath(origlow,orighigh);
+ for (i = p->low; i <= p->high; i++) {
+ if (i == p->low) p->v[i] = tv;
+ else if (i == p->high) p->v[i] = hv;
+ else p->v[i] = model_vnode(modelgraph,orig,i);
+ if (i > p->low)
+ p->e[i-1] = model_vedge(modelgraph,p->v[i-1],p->v[i],orig);
+ }
+ pathinsert(modelgraph,p);
+ }
+ /* merge the weights on the vedges */
+ /* SN: I think this code is only correct if the parallel edges all
+ * refer to the same original endpoint, which is not necessarily true.
+ * However, the correct value will be computed later by subclustports()
+ */
+ old = p->weight; new = ED_weight(orig); tot = old + new;
+ if (origlow == orig->tail) {tp = ED_tailport(orig); hp = ED_headport(orig);}
+ else {tp = ED_headport(orig); hp = ED_tailport(orig);}
+ tx = tp.p.x; if (tx > PORTCLAMP) tx = PORTCLAMP; if (tx < -PORTCLAMP) tx = -PORTCLAMP;
+ hx = hp.p.x; if (hx > PORTCLAMP) hx = PORTCLAMP; if (hx < -PORTCLAMP) hx = -PORTCLAMP;
+ p->tailport = (old/tot)*tx + (new/tot)*p->tailport;
+ p->headport = (old/tot)*hx + (new/tot)*p->headport;
+ p->weight = tot;
+ return p;
+}
+
+/* get model node for an endpoint (can be external to the clust). assumes
+each node and cluster has a unique parent, and nodes have unique names.
+clust and node are from a user graph, not an internal model graph.
+*/
+static Agnode_t *model_getrep(Agraph_t *uclust, Agnode_t *un, Agedge_t *ue)
+{
+ Agraph_t *model = GD_model(uclust);
+ Agraph_t *subclust;
+ vpath_t *skel;
+ Agnode_t *rep;
+
+ if (agcontains(uclust,un)) { /* internal */
+ if (ND_cluster(un) == uclust) { /* primitive */
+ rep = ND_rep(un);
+ if (!rep) {
+ /* i don't believe this should ever really happen because
+ all real nodes were already processed; consider this safety code */
+ rep = model_realnode(model,un);
+ }
+ }
+ else { /* in a subcluster */
+ subclust = subclustof(un,uclust);
+ skel = model_skel(model,subclust);
+ rep = skel->v[ND_rank(un)];
+ }
+ }
+ else { /* external */
+ rep = model_extnode(model,un,ue);
+ }
+ return rep;
+}
+
+static vpath_t *model_skel(Agraph_t *model, Agraph_t *uclust)
+{
+ vpath_t *skel;
+ int i;
+
+ if (!((skel = GD_skel(uclust)))) {
+ skel = GD_skel(uclust) = NEW(vpath_t);
+ skel->key.tail = skel->key.head = NILnode; /* skeletons aren't indexed */
+ skel->low = GD_minrank(uclust);
+ skel->high = GD_maxrank(uclust);
+ skel->v = nodearray(skel->low,skel->high);
+ skel->e = edgearray(skel->low,skel->high);
+ for (i = skel->low; i <= skel->high; i++) {
+ skel->v[i] = model_cnode(model,uclust,i);
+ if (i > skel->low)
+ skel->e[i - 1] = model_cedge(model,skel->v[i-1],skel->v[i]);
+ }
+ }
+ return skel;
+}
+
+static void rec_open_modelgraphs(Agraph_t *ug)
+{
+ Agraph_t *universe, *model, *subg;
+ Agnode_t *n;
+
+ char nametmp[1024]; /* fix this! */
+
+ if (is_a_cluster(ug)) {
+ universe = universegraph(ug);
+ sprintf(nametmp,"model_of_%s",ug->name);
+ model = GD_model(ug) = agsubg(universe,nametmp);
+ if (!(n = agfstnode(ug))) return;
+ GD_minrank(ug) = GD_maxrank(ug) = ND_rank(n);
+ while ((n = agnxtnode(ug,n))) {
+ if (GD_maxrank(ug) < ND_rank(n)) GD_maxrank(ug) = ND_rank(n);
+ if (GD_minrank(ug) > ND_rank(n)) GD_minrank(ug) = ND_rank(n);
+ }
+ GD_minrank(model) = GD_minrank(ug);
+ GD_maxrank(model) = GD_maxrank(ug);
+ }
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_open_modelgraphs(subg);
+}
+
+static void rec_skel(Agraph_t *uclust)
+{
+ Agraph_t *model;
+
+ if (!uclust) return;
+ model = GD_model(uclust);
+ if (GD_skel(model)) return; /* already done ???? */
+ /* skeleton is made within the model of the parent of uclust */
+ if (GD_parent(uclust))
+ (void) model_skel(GD_model(GD_parent(uclust)), uclust);
+ rec_skel(GD_parent(uclust));
+}
+
+static void make_nodes(Agraph_t *ug)
+{
+ Agnode_t *n;
+ Agraph_t *model;
+
+ if ((n = agfstnode(ug)) == NILnode) return;
+ rec_open_modelgraphs(ug);
+ model = GD_model(ug);
+
+ /* the following creates real and cluster skeleton nodes
+ in their original graph order. vnodes get done later. */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ (void) model_realnode(GD_model(ND_cluster(n)),n);
+ if (ND_cluster(n) != ug) rec_skel(ND_cluster(n));
+ }
+}
+
+static int leftmost(Agraph_t *model, int r) { return 0; }
+static int rightmost(Agraph_t *model, int r) {return GD_rank(model)[r].n - 1;}
+
+/* Build a model graph for each cluster + its ext edges.
+ The model is a subgraph of a 'universal graph' for all
+ the objects of a graph and its clusters. This makes it
+ possible to merge the cluster (models) later. Fortunately
+ each primitive node only belongs to one cluster. On the
+ other hand, naming all the virtual objects uniquely is so much
+ useless overhead. (This is one thing libAgraph handles reasonably
+ and that it might make sense to port back to libgraph.)
+
+ Note that the cluster models are not themselves nested subgraphs.
+ This is because we don't want internal objects of a cluster to
+ propagate to its enclosing clusters.
+ */
+static void rec_cluster_model(Agraph_t *ug)
+{
+ graph_t *model,*subg;
+ node_t *n;
+ edge_t *e;
+ node_t *tx, *hx;
+ int minr,maxr,r,*ranksize;
+
+ if (is_a_cluster(ug)) {
+ /* get model graph */
+ model = GD_model(ug);
+
+ /* edges (including external ones) */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstedge(ug->root,n); e; e = agnxtedge(ug->root,e,n)) {
+ tx = model_getrep(ug,e->tail,e);
+ hx = model_getrep(ug,e->head,e);
+ if (tx == hx) continue;
+ /* skip edges that are within some subcluster */
+ if ((ND_type(tx)==NODETYPE_CNODE) && (ND_type(hx)==NODETYPE_CNODE) &&
+ (ND_cluster(tx) == ND_cluster(hx))) continue;
+ (void) model_path(model,tx,hx,e);
+ }
+ }
+
+ /* initialize storage for later installation of nodes */
+
+ /* external nodes can be outside the internal min/max rank bounds */
+ minr = GD_minrank(model);
+ maxr = GD_maxrank(model);
+ for (n = agfstnode(model); n; n = agnxtnode(model,n)) {
+ minr = MIN(minr,ND_rank(n));
+ maxr = MAX(maxr,ND_rank(n));
+ }
+
+ ranksize = intarray(minr,maxr);
+ for (n = agfstnode(model); n; n = agnxtnode(model,n))
+ ranksize[ND_rank(n)]++;
+ GD_rank(model) = T_array(minr,maxr,sizeof(rank_t));
+ for (r = minr; r <= maxr; r++) /* see leftmost() and rightmost() */
+ GD_rank(model)[r].v = N_NEW(ranksize[r]+1,Agnode_t*); /* NIL at end */
+ }
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_model(subg);
+}
+
+static void cluster_model(Agraph_t *ug)
+{
+ Agraph_t *g;
+ Agnode_t *n;
+ Agedge_t *e;
+ int indeg, outdeg;
+
+ rec_cluster_model(ug);
+
+ g = GD_model(ug);
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ indeg = outdeg = 0;
+ if (ND_indeg(n) == 0) {
+ for (e = agfstin(g,n); e; e = agnxtin(g,e)) indeg++;
+ ND_indeg(n) = indeg;
+ }
+ if (ND_outdeg(n) == 0) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) outdeg++;
+ ND_outdeg(n) = outdeg;
+ }
+ GD_maxinoutdeg(g->root) = MAX(GD_maxinoutdeg(g->root),MAX(indeg,outdeg));
+ }
+
+ /* set up mincross running parameters */
+ GD_pass(g) = 0;
+ GD_lastwin(g) = 0;
+ GD_mintry(g) = gvgetint(ug,"minpass",24);
+ GD_maxpass(g) = gvgetint(ug,"maxpass",1024);
+ GD_bestcrossings(g) = MAXINT;
+}
+
+static Agraph_t *universegraph(Agraph_t *ug)
+{
+ graph_t *root = ug->root;
+ graph_t *univ,*model;
+ if ((model = GD_model(root)))
+ univ = model->root;
+ else {
+ univ = agopen("_universe\001_",AGDIGRAPHSTRICT);
+ }
+ return univ;
+}
+
+static void flat_edges(Agraph_t *clust)
+{
+#ifdef NOTDEF
+ for (n = agfstnode(clust); n; n = agnxtnode(clust)) {
+ for (e = agfstedge(root,n); e; e = agnxtedge(root,e,n)) {
+ }
+ }
+ ordered_edges();
+#endif
+}
+
+static void search_component(Agraph_t *g, Agnode_t *n, int c)
+{
+ Agedge_t *e;
+ ND_component(n) = c;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (ND_component(e->head) < 0)
+ search_component(g,e->head,c);
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (ND_component(e->tail) < 0)
+ search_component(g,e->tail,c);
+}
+
+static int ND_comp_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_component(*(Agnode_t**)arg0) - ND_component(*(Agnode_t**)arg1);
+}
+
+static component_t build_components(Agraph_t *g, boolean down)
+{
+ component_t rv;
+ node_t *n;
+ int r, rootcnt, compcnt, deg;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_component(n) = -1; /* initialize to unknown component */
+
+ compcnt = 0; rootcnt = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ /* set priority for subsequent BFS to install nodes, and record roots */
+ if (down) deg = ND_indeg(n);
+ else deg = ND_outdeg(n);
+ ND_priority(n) = deg;
+ if (deg == 0) rootcnt++;
+ /* count and mark components */
+ if (ND_component(n) < 0) search_component(g,n,compcnt++);
+ }
+
+ rv.n = compcnt;
+ rv.r = rootcnt;
+ rv.root = N_NEW(rv.r,Agnode_t*);
+ r = 0;
+ /* install roots in root list */
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ if (ND_priority(n) == 0) rv.root[r++] = n;
+ /* sort root list so components are contiguous */
+ qsort(rv.root,rv.n,sizeof(node_t*),ND_comp_cmpf);
+ return rv;
+}
+
+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++;
+}
+
+/*
+ populates rank lists of g. there are some key details:
+ 1) the input graph ordering must be respected (in left to right initialization)
+ 2) connected components are separated and marked with indices
+ 3) series-parallel graphs (includes trees, obviously) must not have crossings
+*/
+static void build_ranks(Agraph_t *g, boolean down)
+{
+ queue *q;
+ component_t c;
+ int r;
+ Agnode_t *n;
+ Agedge_t *e;
+
+ c = build_components(g, down);
+
+ /* process each each component */
+ q = new_queue(agnnodes(g)+1);
+ for (r = 0; r < c.r; r++) {
+ enqueue(q,c.root[r]);
+ if ((r + 1 >= c.r)||(ND_component(c.root[r])!=ND_component(c.root[r+1]))) {
+ while ((n = dequeue(q))) {
+ install(g,n);
+ if (down) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (--ND_priority(e->head) == 0) enqueue(q,e->head);
+ }
+ else {
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (--ND_priority(e->tail) == 0) enqueue(q,e->head);
+ }
+ }
+ }
+ }
+ free_queue(q);
+}
+
+/* this predicate indicates if mincross should be run on this cluster */
+static boolean run(Agraph_t *mg)
+{
+ if (GD_pass(mg) > GD_maxpass(mg)) return FALSE;
+ if (GD_pass(mg) - GD_lastwin(mg) > GD_mintry(mg)) return FALSE;
+ GD_pass(mg) = GD_pass(mg) + 1;
+ return TRUE;
+}
+
+static int presort_cmpf(const void *arg0, const void *arg1)
+{
+ Agnode_t *n0, *n1;
+ Agraph_t *c0, *c1;
+
+ n0 = *(Agnode_t**)arg0;
+ n1 = *(Agnode_t**)arg1;
+ c0 = ND_cluster(n0);
+ c1 = ND_cluster(n1);
+ if (c0 == c1) return 0;
+ assert(ND_rank(n0) == ND_rank(n1));
+ n0 = GD_skel(c0)->v[ND_rank(n0)];
+ n1 = GD_skel(c1)->v[ND_rank(n1)];
+ return ND_order(n0) - ND_order(n1);
+}
+
+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;
+ }
+}
+
+/* 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 = pathdict(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;
+ }
+ }
+}
+
+static void mincross_clust(Agraph_t *ug)
+{
+ Agraph_t *g;
+ g = GD_model(ug);
+ if (run(g)) {
+ presort(ug); /* move the external nodes */
+ subclustports(ug);
+ do {
+ mincross_sweep(g,GD_pass(g)%2,GD_pass(g)%4<2);
+ } while (run(g));
+ transpose_sweep(g,TRUE);
+ restorebest(g);
+ }
+}
+
+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));
+}
+
+/* this assumes one level per node - no mega-nodes */
+static void apply_model(Agraph_t *ug)
+{
+ Agnode_t *un;
+ for (un = agfstnode(ug); un; un = agnxtnode(ug,un))
+ ND_order(un) = ND_order(ND_rep(un));
+}
+
+/* this is a first cut at a top-level planner. it's lame. */
+static void rec_cluster_run(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) mincross_clust(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_run(subg);
+ if (is_a_cluster(ug)) mincross_clust(ug);
+}
+
+/* this is the top level mincross entry point */
+void dot_mincross(Agraph_t *user)
+{
+ rec_cluster_init(user);
+ rec_cluster_run(user);
+ globalopt(user);
+ apply_model(user);
+}
+
+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;
+}
+
+/* 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);
+}
+
+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;
+}
+
+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 mincross_sweep(Agraph_t* g, int dir, boolean reverse)
+{
+ int r,other,low,high,first,last;
+ int hasfixed;
+
+ low = GD_minrank(g);
+ high = GD_maxrank(g);
+ if (dir == 0) return;
+ if (dir > 0) { first = low + 1; last = high; dir = 1;} /* down */
+ else { first = high - 1; last = low; dir = -1;} /* up */
+
+ for (r = first; r != last + dir; r += dir) {
+ other = r - dir;
+ hasfixed = medians(g,r,other);
+ reorder(g,r,reverse,hasfixed);
+ }
+ transpose_sweep(g,NOT(reverse));
+ savebest(g);
+}
+
+
+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;
+}
+
+static void build_flat_graphs(Agraph_t *g)
+{
+}
+
+static int out_cross(node_t *v, node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0,t;
+
+ for (e2 = agfstout(w->graph,w); e2; e2 = agnxtout(w->graph,e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(e2->head);
+ for (e1 = agfstout(v->graph,v); e1; e1 = agnxtout(v->graph,e1)) {
+ t = ND_order(e1->head) - inv;
+ if ((t > 0) || ((t == 0) && (ED_headport(e1).p.x > ED_headport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int in_cross(node_t *v,node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0, t;
+
+ for (e2 = agfstin(w->graph,w); e2; e2 = agnxtin(w->graph,e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(e2->tail);
+ for (e1 = agfstin(v->graph,v); e1; e1 = agnxtin(v->graph,e1)) {
+ t = ND_order(e1->tail) - inv;
+ if ((t > 0) || ((t == 0) && (ED_tailport(e1).p.x > ED_tailport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int int_cmpf(const void *arg0, const void *arg1)
+{
+ return *(int*)arg0 - *(int*)arg1;
+}
+
+
+/* 8 is the number of bits in port.order, an unsigned char */
+#define VAL(node,port) (((node)->u.order << 8) + (port).order)
+
+/*
+ * 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 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 savebest(graph_t *g)
+{
+ int nc;
+ Agnode_t *n;
+
+ nc = crossings(g);
+ if (nc < GD_bestcrossings(g)) {
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_saveorder(n) = ND_order(n);
+ GD_bestcrossings(g) = nc;
+ GD_lastwin(g) = GD_pass(g);
+ }
+}
+
+static int ND_order_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_order(*(Agnode_t**)arg0) - ND_order(*(Agnode_t**)arg1);
+}
+
+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 int crossings(graph_t *g)
+{
+ int i, rv;
+
+ rv = 0;
+ for (i = GD_minrank(g); i < GD_maxrank(g); i++) {
+ rv += crossings_below(g,i);
+ }
+ return rv;
+}
+
+/* returns level of (model) node's cluster */
+static int lev(Agnode_t *n)
+{
+ if (!n) return -1;
+ return (GD_level(ND_cluster(n)));
+}
+
+/*
+ * allocates a vpath_t per original user edge. later the path
+ * contents will be set to define vnode chains of intercluster edges.
+ */
+static void interclusterpaths(Agraph_t *ug, Agraph_t *topmodel)
+{
+ Agnode_t *n, *n0;
+ Agedge_t *e, *ue;
+ vpath_t *p;
+
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(ug,n); e; e = agnxtout(ug,e)) {
+ if (ND_cluster(e->tail) != ND_cluster(e->head)) {
+ if (!(p = pathsearch(topmodel->root,e->tail,e->head))) {
+ p = newpath(e->tail, e->head);
+ pathinsert(topmodel->root,p);
+ }
+ }
+ }
+ }
+
+ /* scan all vnodes of model graph and make decisions */
+ for (n = agfstnode(topmodel); n; n = agnxtnode(topmodel,n)) {
+ if (ND_type(n) == NODETYPE_VNODE) {
+ ue = ND_erep(n);
+ p = pathsearch(topmodel->root,ue->tail,ue->head);
+ if (p) {
+ if (!(n0 = p->v[ND_rank(n)]) || (lev(n0) < lev(n)))
+ p->v[ND_rank(n)] = n;
+ }
+ }
+ }
+ /* need to remove the other edges of these vnodes!! */
+}
+
+/* build the global (flat) graph of the universe. this is 'flat'
+in the sense that there is one data structure for the entire graph
+(not 'flat' in the sense of flat edges within the same level.)
+*/
+static rank_t *globalize(Agraph_t *user, Agraph_t *topmodel)
+{
+ rank_t *globrank;
+ int minr,maxr,r;
+
+ /* setup bookkeeping */
+ interclusterpaths(user, topmodel);
+
+ /* allocate global ranks */
+ minr = GD_minrank(topmodel);
+ maxr = GD_maxrank(topmodel);
+ globrank = T_array(minr,maxr,sizeof(rank_t));
+ countup(user,globrank);
+ for (r = minr; r <= maxr; r++) {
+ globrank[r].v = N_NEW(globrank[r].n+1,Agnode_t*); /* NIL at end */
+ globrank[r].n = 0; /* reset it */
+ }
+
+ /* installation */
+ for (r = minr; r <= maxr; r++)
+ installglob(user,topmodel,globrank,r);
+
+ removejunk(user, topmodel);
+ reconnect(user, topmodel);
+
+ /* optimization */
+ return globrank;
+}
+
+
+static void countup(Agraph_t *g, rank_t *globr)
+{
+ Agnode_t *n;
+ Agedge_t *e;
+ int r0, r1, low, high, i;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ globr[ND_rank(n)].n += 1;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
+ r0 = ND_rank(e->tail);
+ r1 = ND_rank(e->head);
+ low = MIN(r0,r1);
+ high = MAX(r0,r1);
+ for (i = low + 1; i < high; i++)
+ globr[i].n += 1;
+ }
+ }
+}
+
+/* install nodes from rank r of (g or its clusters) into globr. */
+static void installglob(Agraph_t *ug, Agraph_t *fromgraph, rank_t *globr, int r)
+{
+ rank_t *myrank;
+ int i;
+ Agnode_t *v;
+ Agedge_t *uedge;
+ vpath_t *path;
+
+ if (is_a_cluster(fromgraph)) {
+ myrank = &GD_rank(fromgraph)[r];
+ i = 0;
+ while ((v = myrank->v[i++])) {
+ switch (ND_type(v)) {
+ case NODETYPE_REAL:
+ /* install primitive nodes */
+ globr[r].v[globr[r].n++] = v;
+ ND_isglobal(v) = TRUE;
+ break;
+ case NODETYPE_VNODE:
+ /* install vnode for non-intercluster edges, and for intercluster
+ edges if this vnode was chosen as the path representative */
+ uedge = ND_erep(v);
+ path = pathsearch(fromgraph->root,uedge->tail,uedge->head);
+ if (!path || (path->v[r] == v)) {
+ globr[r].v[globr[r].n++] = v;
+ ND_isglobal(v) = TRUE;
+ }
+ break;
+ case NODETYPE_CNODE:
+ /* install clusters recursively */
+ installglob(ug,ND_cluster(v),globr,r);
+ break;
+ case NODETYPE_XNODE:
+ /* we're ignoring these */
+ break;
+ }
+ }
+ }
+}
+
+/*
+ after making the global graph, delete all non-global objects.
+ */
+static void removejunk(Agraph_t *ug, Agraph_t *topmodel)
+{
+ Agnode_t *v,*vv;
+
+ for (v = agfstnode(topmodel); v; v = vv) {
+ vv = agnxtnode(topmodel,v);
+ if (!ND_isglobal(v)) agdelete(topmodel,v);
+ }
+}
+
+/*
+ fix up the user edge paths in the global graph.
+*/
+static void reconnect(Agraph_t *ug, Agraph_t *topmodel)
+{
+ Agnode_t *n, *n0, *n1;
+ Agedge_t *e, *e0;
+ vpath_t *p;
+ int i;
+
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(ug,n); e; e = agnxtout(ug,e)) {
+ if (ND_cluster(e->tail) != ND_cluster(e->head)) {
+ p = pathsearch(topmodel->root,e->tail,e->head);
+ n0 = p->v[p->low];
+ for (i = p->low + 1; i <= p->high; i++) {
+ n1 = p->v[i];
+ if (!(e0 = p->e[i])) e0 = agedge(topmodel,n0,n1);
+ ED_weight(e0) += ED_weight(e);
+ ED_xpenalty(e0) += ED_xpenalty(e);
+ n0 = n1;
+ }
+ }
+ }
+ }
+}
+
+#ifdef NOTDEF
+/* (no parallel edges, all forward edges, flat cycles broken) */
+void build_main_graph(Agraph_t *g)
+{
+ Agraph_t *gprime,*gprimeflat;
+ Agnode_t *v,*vprime;
+ Agedge_t *eprev;
+
+ gprime = agopen("model",AGDIGRAPHSTRICT);
+ for (v = agfstnode(g); v; v = agnxtnode(g,v)) {
+ vprime = agnode(gprime,v->name);
+ for (e = agfstout(v); e; e = agxntout(g,e)) {
+ tail = e->tail;
+ head = e->head;
+ if (ND_rank(tail) > ND_rank(head)) {Agnode_t *t = head; head = tail; tail = t;}
+ if (!(eprev = agfindedge(gprime,tail,head))) eprev = agedge(gprime,tail,head);
+ merge(eprev,e);
+ associate(e,eprev);
+ }
+ }
+ break_cycles(gprime);
+}
+#endif
+
+/* I. initialization */
+static void cluster_init(Agraph_t *ug)
+
+{
+ Agraph_t *mg;
+
+ make_nodes(ug);
+ cluster_model(ug);
+ mg = GD_model(ug);
+ flat_edges(mg);
+ build_ranks(mg,TRUE);
+}
+
+static void rec_cluster_init(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) cluster_init(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_init(subg);
+}
--- /dev/null
+#include "newdot.h"
+
+/* given:
+ * each node and cluster has a unique containing cluster.
+ * ND_level is the integer level of each node.
+ * ND_cluster is the lowest containing cluster of each node.
+ * ED_xpenalty is the crossing weight for an edge.
+ * find:
+ * GD_rank - the levels of the graph
+ * ND_order - the global order of each node
+ */
+
+typedef struct component_s {
+ int n;
+ node_t **root;
+ int r;
+} component_t;
+
+static Agraph_t *subclustof(Agnode_t *n, Agraph_t *clust);
+static void *T_array(int low, int high, int size);
+static Agnode_t **nodearray(int low, int high);
+static int *intarray(int low, int high);
+static Agraph_t *cluster_model(Agraph_t *clust);
+static void flat_edges(Agraph_t *clust);
+static void search_component(Agraph_t *g, Agnode_t *n, int c);
+static int ND_comp_cmpf(const void *arg0, const void *arg1);
+static component_t build_components(Agraph_t *g, boolean down);
+static void install(Agraph_t *g, Agnode_t *n);
+static void build_ranks(Agraph_t *ug, boolean down);
+static void cluster_init(Agraph_t *userclust);
+static void rec_cluster_init(Agraph_t *userclust);
+static boolean run(Agraph_t *g);
+static void mincross_clust(Agraph_t *clust);
+static void globalopt(Agraph_t *ug);
+static void rec_cluster_run(Agraph_t *ug);
+static void exchange(Agraph_t *g, Agnode_t *u, Agnode_t *v);
+static void transpose_sweep(graph_t* g, int reverse);
+static void mincross_sweep(graph_t* g, int dir, boolean reverse);
+static int left2right(graph_t *g, node_t *v, node_t *w);
+static void build_flat_graphs(graph_t *g);
+static boolean medians(graph_t *g, int r0, int r1);
+static void reorder(graph_t *g, int r, boolean reverse, boolean hasfixed);
+static void savebest(graph_t *g);
+static void restorebest(graph_t *g);
+static int crossings(graph_t *g);
+static int in_cross(node_t *v,node_t *w);
+static int out_cross(node_t *v,node_t *w);
+static rank_t *globalize(Agraph_t *user, Agraph_t *model);
+static void installglob(Agraph_t *user, Agraph_t *fromgraph, rank_t *globr, int r);
+static void countup(Agraph_t *g, rank_t *globr);
+
+static Agnode_t *model_getrep(Agraph_t *ug, Agnode_t *n, Agedge_t *e);
+static Agraph_t *universegraph(Agraph_t *ug);
+static vpath_t *model_skel(Agraph_t *uclust, Agraph_t *model);
+static void clusterchains(Agraph_t *model, Agraph_t *ug);
+static void removejunk(Agraph_t *ug, Agraph_t *topmodel);
+static void reconnect(Agraph_t *ug, Agraph_t *topmodel);
+
+/* from level.c - eventually clean this up. */
+static int is_a_cluster(graph_t *g)
+{
+ return ((g == g->root) || (!strncasecmp(g->name,"cluster",7)));
+}
+
+/* find the cluster of n that is an immediate child of g */
+static Agraph_t *subclustof(Agnode_t *n, Agraph_t *g)
+{
+ Agraph_t *rv;
+ for (rv = ND_cluster(n); rv && (GD_parent(rv) != g); rv = GD_parent(rv));
+ return rv;
+}
+
+static void *T_array(int low, int high, int size)
+{
+ char *rv;
+
+ rv = calloc((high - low + 1),size);
+ rv = rv - (low * size);
+ return rv;
+}
+
+static Agnode_t **nodearray(int low, int high)
+{ return (Agnode_t**) T_array(low,high,sizeof(Agnode_t*)); }
+
+static Agedge_t **edgearray(int low, int high)
+{ return (Agedge_t**) T_array(low,high,sizeof(Agedge_t*)); }
+
+static int *intarray(int low, int high)
+{ return (int*) T_array(low,high,sizeof(int)); }
+
+static Agnode_t *model_realnode(Agraph_t *modelgraph, Agnode_t *orig)
+{
+ Agnode_t *rep;
+ /* this is OK because realnode can only be in one model graph/subgraph */
+ rep = agnode(modelgraph,orig->name);
+ ND_rank(rep) = ND_rank(orig);
+ ND_cluster(rep) = modelgraph;
+ ND_vrep(orig) = rep;
+ ND_vrep(rep) = orig;
+ ND_type(rep) = NODETYPE_REAL;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agnode_t *model_extnode(Agraph_t *modelgraph, Agnode_t *orig, Agedge_t *e)
+{
+ char name[1024];
+ Agnode_t *rep;
+
+ sprintf(name,"ext_%s_%s",modelgraph->name,orig->name);
+ /* can't be anonymous - needs to be repeatable */
+ rep = agnode(modelgraph,name);
+ ND_rank(rep) = ND_rank(orig);
+ ND_cluster(rep) = modelgraph;
+ ND_erep(rep) = e;
+ ND_type(rep) = NODETYPE_XNODE;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agnode_t *model_vnode(Agraph_t *modelgraph, Agedge_t *longedge, int level)
+{
+ Agnode_t *rep;
+ rep = agnode(modelgraph,0);
+ ND_rank(rep) = level;
+ ND_cluster(rep) = modelgraph;
+ ND_erep(rep) = longedge;
+ ND_type(rep) = NODETYPE_VNODE;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agnode_t *model_cnode(Agraph_t *modelgraph, Agraph_t *clust, int level)
+{
+ Agnode_t *rep;
+ rep = agnode(modelgraph,0);
+ ND_rank(rep) = level;
+ ND_cluster(rep) = clust;
+ ND_grep(rep) = clust;
+ ND_type(rep) = NODETYPE_CNODE;
+ /* other initializations may need to go here */
+ return rep;
+}
+
+static Agedge_t *model_vedge(Agraph_t *modelgraph, Agnode_t *tx, Agnode_t *hx, Agedge_t *orig)
+{
+ Agedge_t *e;
+
+ e = agedge(modelgraph,tx,hx);
+ ED_xpenalty(e) = VEDGE_PENALTY * ED_xpenalty(orig);
+ ED_weight(e) = ED_weight(orig);
+ return e;
+}
+
+static Agedge_t *model_cedge(Agraph_t *modelgraph, Agnode_t *tx, Agnode_t *hx)
+{
+ Agedge_t *e;
+
+ e = agedge(modelgraph,tx,hx);
+ ED_xpenalty(e) = CEDGE_PENALTY;
+ ED_weight(e) = 1;
+ return e;
+}
+
+static void getlowhigh(Agnode_t **n0, Agnode_t **n1)
+{
+ Agnode_t *temp;
+ if (ND_rank(*n0) > ND_rank(*n1))
+ {temp = *n0; *n0 = *n1; *n1 = temp;}
+}
+
+vpath_t *newpath(Agnode_t *u, Agnode_t *v)
+{
+ Agnode_t *origlow = u, *orighigh = v;
+ vpath_t *p;
+
+ getlowhigh(&origlow,&orighigh);
+ p = NEW(vpath_t);
+ p->key.tail = origlow;
+ p->key.head = orighigh;
+ p->low = ND_rank(origlow);
+ p->high = ND_rank(orighigh);
+ p->v = nodearray(p->low, p->high);
+ p->e = edgearray(p->low, p->high);
+ return p;
+}
+
+static int pathcmp(Dt_t *d, void *arg0, void *arg1, Dtdisc_t *disc)
+{
+ int rv;
+ if ((rv = ((vpath_t*)arg0)->key.tail - ((vpath_t*)arg1)->key.tail) == 0)
+ rv = ((vpath_t*)arg0)->key.head - ((vpath_t*)arg1)->key.head;
+ return rv;
+}
+
+static Dtdisc_t Vpathdisc = {
+ 0, /* pass whole object as key */
+ 0, /* key size and type */
+ -1, /* link offset */
+ (Dtmake_f)0,
+ (Dtfree_f)0,
+ (Dtcompar_f) pathcmp,
+ (Dthash_f)0,
+ (Dtmemory_f)0,
+ (Dtevent_f)0
+};
+
+static Dict_t *pathdict(Agraph_t *g)
+{
+ Dict_t *dict;
+ dict = GD_pathdict(g);
+ if (!dict) dict = GD_pathdict(g) = dtopen(&Vpathdisc,Dttree);
+ return dict;
+}
+
+static vpath_t *pathsearch(Agraph_t *g, Agnode_t *u, Agnode_t *v)
+{
+ vpath_t *p,path;
+ Dict_t *dict;
+ Agnode_t *origlow = u, *orighigh = v;
+
+ getlowhigh(&origlow, &orighigh);
+ dict = pathdict(g);
+ path.key.tail = origlow;
+ path.key.head = orighigh;
+ p = dtsearch(dict,&path);
+ return p;
+}
+
+static void pathinsert(Agraph_t *g, vpath_t *p)
+{
+ Dict_t *dict;
+ dict = pathdict(g);
+ p = dtinsert(dict,p);
+}
+
+vpath_t *model_path(Agraph_t *modelgraph, Agnode_t *tx, Agnode_t *hx, Agedge_t *orig)
+{
+ vpath_t *p;
+ Agnode_t *origlow, *orighigh;
+ int i;
+
+ getlowhigh(&tx,&hx);
+ origlow = orig->tail; orighigh = orig->head;
+ getlowhigh(&origlow,&orighigh);
+
+ p = pathsearch(modelgraph,origlow,orighigh);
+ if (!p) {
+ p = newpath(origlow,orighigh);
+ for (i = p->low; i <= p->high; i++) {
+ if (i == p->low) p->v[i] = tx;
+ else if (i == p->high) p->v[i] = hx;
+ else p->v[i] = model_vnode(modelgraph,orig,i);
+ if (i > p->low)
+ p->e[i] = model_vedge(modelgraph,p->v[i-1],p->v[i],orig);
+ }
+ pathinsert(modelgraph,p);
+ }
+ return p;
+}
+
+/* get model node for an endpoint (can be external to the clust). assumes
+each node and cluster has a unique parent, and nodes have unique names.
+clust and node are from a user graph, not an internal model graph.
+*/
+static Agnode_t *model_getrep(Agraph_t *uclust, Agnode_t *un, Agedge_t *ue)
+{
+ Agraph_t *model = GD_model(uclust);
+ Agraph_t *subclust;
+ vpath_t *skel;
+ Agnode_t *rep;
+
+ if (agcontains(uclust,un)) { /* internal */
+ if (ND_cluster(un) == uclust) { /* primitive */
+ rep = ND_rep(un);
+ if (!rep) {
+ /* i don't believe this should ever really happen because
+ all real nodes were already processed; consider this safety code */
+ rep = model_realnode(model,un);
+ }
+ }
+ else { /* in a subcluster */
+ subclust = subclustof(un,uclust);
+ skel = model_skel(model,subclust);
+ rep = skel->v[ND_rank(un)];
+ }
+ }
+ else { /* external */
+ rep = model_extnode(model,un,ue);
+ }
+ return rep;
+}
+
+static vpath_t *model_skel(Agraph_t *model, Agraph_t *uclust)
+{
+ vpath_t *skel;
+ int i;
+
+ if (!((skel = GD_skel(uclust)))) {
+ skel = GD_skel(uclust) = NEW(vpath_t);
+ skel->key.tail = skel->key.head = NILnode; /* skeletons aren't indexed */
+ skel->low = GD_minrank(uclust);
+ skel->high = GD_maxrank(uclust);
+ skel->v = nodearray(skel->low,skel->high);
+ skel->e = edgearray(skel->low,skel->high);
+ for (i = skel->low; i <= skel->high; i++) {
+ skel->v[i] = model_cnode(model,uclust,i);
+ if (i > skel->low)
+ skel->e[i - 1] = model_cedge(model,skel->v[i-1],skel->v[i]);
+ }
+ }
+ return skel;
+}
+
+static void scangraph(Agraph_t *g)
+{
+ Agnode_t *n;
+ Agedge_t *e;
+ short indeg, outdeg;
+
+ if ((n = agfstnode(g)) == NILnode) return;
+
+ model = universegraph(g);
+ GD_minrank(g) = GD_maxrank(g) = ND_rank(n);
+ GD_maxinoutdeg(g) = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ if (GD_maxrank(g) < ND_rank(n)) GD_maxrank(g) = ND_rank(n);
+ if (GD_minrank(g) > ND_rank(n)) GD_minrank(g) = ND_rank(n);
+ if (ND_indeg(n) == 0) {
+ indeg = 0; for (e = agfstin(g,n); e; e = agnxtin(g,e)) indeg++;
+ ND_indeg(n) = indeg;
+ }
+ if (ND_outdeg(n) == 0) {
+ outdeg = 0; for (e = agfstout(g,n); e; e = agnxtout(g,e)) outdeg++;
+ ND_outdeg(n) = outdeg;
+ }
+ GD_maxinoutdeg(g) = MAX(GD_maxinoutdeg(g),MAX(indeg,outdeg));
+ }
+}
+
+/* Build a model graph for each cluster + its ext edges.
+ The model is a subgraph of a 'universal graph' for all
+ the objects of a graph and its clusters. This makes it
+ possible to merge the cluster (models) later. Fortunately
+ each primitive node only belongs to one cluster. On the
+ other hand, naming all the virtual objects uniquely is so much
+ useless overhead. (This is one thing libAgraph handles reasonably
+ and that it might make sense to port back to libgraph.)
+
+ Note that the cluster models are not thesmselves nested subgraphs.
+ This is because we don't want internal objects of a cluster to
+ propagate to its enclosing clusters.
+ */
+static Agraph_t *cluster_model(Agraph_t *ug)
+{
+ graph_t *model,*universe;
+ node_t *n;
+ edge_t *e;
+ node_t *tx, *hx;
+ int minr,maxr,r,*ranksize;
+
+ /* get model graph */
+ if (GD_model(ug)) abort(); /* double initialization? not yet re-entrant. */
+ universe = universegraph(ug);
+ model = GD_model(ug) = agsubg(universe,ug->name);
+
+ /* create nodes explicitly. this copies the search order of original nodes
+ and ensures singletons (including those in subclusters) are created */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ if (ND_cluster(n) == ug) (void) model_realnode(model,n);
+ else clusterchains(model,subclustof(n,ug));
+ }
+
+ /* edges (including external ones) */
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstedge(ug->root,n); e; e = agnxtedge(ug->root,e,n)) {
+ tx = model_getrep(ug,e->tail,e);
+ hx = model_getrep(ug,e->head,e);
+ if (tx == hx) continue;
+ /* skip edges that are within some subcluster */
+ if ((ND_type(tx) == NODETYPE_CNODE) && (ND_type(hx) == NODETYPE_CNODE) &&
+ (ND_cluster(tx) == ND_cluster(hx))) continue;
+ (void) model_path(model,tx,hx,e);
+ }
+ }
+
+ /* initialize storage for ranks in the model; nodes are installed later */
+ GD_minrank(model) = GD_minrank(ug);
+ GD_maxrank(model) = GD_maxrank(ug);
+ minr = GD_minrank(model);
+ maxr = GD_maxrank(model);
+ ranksize = intarray(minr,maxr);
+ for (n = agfstnode(model); n; n = agnxtnode(model,n))
+ ranksize[ND_rank(n)]++;
+ GD_rank(model) = T_array(minr,maxr,sizeof(rank_t));
+ for (r = minr; r <= maxr; r++)
+ GD_rank(model)[r].v = N_NEW(ranksize[r]+1,Agnode_t*); /* NIL at end */
+
+ /* set up mincross running parameters */
+ GD_pass(model) = 0;
+ GD_lastwin(model) = 0;
+ GD_mintry(model) = gvgetint(ug,"minpass",24);
+ GD_maxpass(model) = gvgetint(ug,"maxpass",1024);
+ GD_bestcrossings(model) = MAXINT;
+ return model;
+}
+
+static Agraph_t *universegraph(Agraph_t *ug)
+{
+ graph_t *root = ug->root;
+ graph_t *univ,*model;
+ if ((model = GD_model(root)))
+ univ = model->root;
+ else {
+ univ = agopen("_universe\001_",AGDIGRAPHSTRICT);
+ }
+ return univ;
+}
+
+static void clusterchains(Agraph_t *model, Agraph_t *ug)
+{
+ if (GD_clusterchainsdone(ug)) return;
+ if (is_a_cluster(ug))
+ (void) model_skel(model,ug);
+ GD_clusterchainsdone(ug) = TRUE;
+}
+
+static void flat_edges(Agraph_t *clust)
+{
+#ifdef NOTDEF
+ for (n = agfstnode(clust); n; n = agnxtnode(clust)) {
+ for (e = agfstedge(root,n); e; e = agnxtedge(root,e,n)) {
+ }
+ }
+ ordered_edges();
+#endif
+}
+
+static void search_component(Agraph_t *g, Agnode_t *n, int c)
+{
+ Agedge_t *e;
+ ND_component(n) = c;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (ND_component(e->head) < 0)
+ search_component(g,e->head,c);
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (ND_component(e->tail) < 0)
+ search_component(g,e->tail,c);
+}
+
+static int ND_comp_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_component(*(Agnode_t**)arg0) - ND_component(*(Agnode_t**)arg1);
+}
+
+static component_t build_components(Agraph_t *g, boolean down)
+{
+ component_t rv;
+ node_t *n;
+ int r, rootcnt, compcnt, deg;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_component(n) = -1; /* initialize to unknown component */
+
+ compcnt = 0; rootcnt = 0;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ /* set priority for subsequent BFS to install nodes, and record roots */
+ if (down) deg = ND_indeg(n);
+ else deg = ND_outdeg(n);
+ ND_priority(n) = deg;
+ if (deg == 0) rootcnt++;
+ /* count and mark components */
+ if (ND_component(n) < 0) search_component(g,n,compcnt++);
+ }
+
+ rv.n = compcnt;
+ rv.r = rootcnt;
+ rv.root = N_NEW(rv.r,Agnode_t*);
+ r = 0;
+ /* install roots in root list */
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ if (ND_priority(n) == 0) rv.root[r++] = n;
+ /* sort root list so components are contiguous */
+ qsort(rv.root,rv.n,sizeof(node_t*),ND_comp_cmpf);
+ return rv;
+}
+
+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++;
+}
+
+/*
+ populates rank lists of g. there are some key details:
+ 1) the input graph ordering must be respected (in left to right initialization)
+ 2) connected components are separated and marked with indices
+ 3) series-parallel graphs (includes trees, obviously) must not have crossings
+*/
+static void build_ranks(Agraph_t *g, boolean down)
+{
+ queue *q;
+ component_t c;
+ int r;
+ Agnode_t *n;
+ Agedge_t *e;
+
+ c = build_components(g, down);
+
+ /* process each each component */
+ q = new_queue(agnnodes(g)+1);
+ for (r = 0; r < c.r; r++) {
+ enqueue(q,c.root[r]);
+ if ((r + 1 >= c.r)||(ND_component(c.root[r])!=ND_component(c.root[r+1]))) {
+ while ((n = dequeue(q))) {
+ install(g,n);
+ if (down) {
+ for (e = agfstout(g,n); e; e = agnxtout(g,e))
+ if (--ND_priority(e->head) == 0) enqueue(q,e->head);
+ }
+ else {
+ for (e = agfstin(g,n); e; e = agnxtin(g,e))
+ if (--ND_priority(e->tail) == 0) enqueue(q,e->head);
+ }
+ }
+ }
+ }
+ free_queue(q);
+}
+
+static boolean run(Agraph_t *mg)
+{
+ if (GD_pass(mg) > GD_maxpass(mg)) return FALSE;
+ if (GD_pass(mg) - GD_lastwin(mg) > GD_mintry(mg)) return FALSE;
+ GD_pass(mg) = GD_pass(mg) + 1;
+ return TRUE;
+}
+
+static void mincross_clust(Agraph_t *ug)
+{
+ Agraph_t *g;
+ g = GD_model(ug);
+ if (run(g)) {
+ do {
+ mincross_sweep(g,GD_pass(g)%2,GD_pass(g)%4<2);
+ } while (run(g));
+ transpose_sweep(g,TRUE);
+ restorebest(g);
+ }
+}
+
+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));
+}
+
+/* this assumes one level per node - no mega-nodes */
+static void apply_model(Agraph_t *g)
+{
+ Agnode_t *n;
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_order(n) = ND_order(ND_rep(n));
+}
+
+/* this is a first cut at a top-level planner. it's lame. */
+static void rec_cluster_run(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) mincross_clust(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_run(subg);
+ if (is_a_cluster(ug)) mincross_clust(ug);
+}
+
+/* this is the top level mincross entry point */
+void dot_mincross(Agraph_t *user)
+{
+ rec_cluster_init(user);
+ rec_cluster_run(user);
+ globalopt(user);
+ apply_model(user);
+}
+
+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;
+}
+
+/* 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);
+}
+
+int transpose_onerank(graph_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 = 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 > 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;
+}
+
+static void transpose_sweep(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;
+ 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 mincross_sweep(graph_t* g, int dir, boolean reverse)
+{
+ int r,other,low,high,first,last;
+ int hasfixed;
+
+ low = GD_minrank(g);
+ high = GD_maxrank(g);
+ if (dir == 0) return;
+ if (dir > 0) { first = low + 1; last = high; dir = 1;} /* down */
+ else { first = high - 1; last = low; dir = -1;} /* up */
+
+ for (r = first; r != last + dir; r += dir) {
+ other = r - dir;
+ hasfixed = medians(g,r,other);
+ reorder(g,r,reverse,hasfixed);
+ }
+ transpose_sweep(g,NOT(reverse));
+ savebest(g);
+}
+
+
+static int left2right(graph_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;
+}
+
+static void build_flat_graphs(graph_t *g)
+{
+}
+
+static int out_cross(node_t *v, node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0,t;
+
+ for (e2 = agfstout(w->graph,w); e2; e2 = agnxtout(w->graph,e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(e2->head);
+ for (e1 = agfstout(v->graph,v); e1; e1 = agnxtout(v->graph,e1)) {
+ t = ND_order(e1->head) - inv;
+ if ((t > 0) || ((t == 0) && (ED_headport(e1).p.x > ED_headport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int in_cross(node_t *v,node_t *w)
+{
+ register edge_t *e1,*e2;
+ register int inv, cross = 0, t;
+
+ for (e2 = agfstin(w->graph,w); e2; e2 = agnxtin(w->graph,e2)) {
+ register int cnt = ED_xpenalty(e2);
+ inv = ND_order(e2->tail);
+ for (e1 = agfstin(v->graph,v); e1; e1 = agnxtin(v->graph,e1)) {
+ t = ND_order(e1->tail) - inv;
+ if ((t > 0) || ((t == 0) && (ED_tailport(e1).p.x > ED_tailport(e2).p.x)))
+ cross += ED_xpenalty(e1) * cnt;
+ }
+ }
+ return cross;
+}
+
+static int int_cmpf(const void *arg0, const void *arg1)
+{
+ return *(int*)arg0 - *(int*)arg1;
+}
+
+
+/* 8 is the number of bits in port.order, an unsigned char */
+#define VAL(node,port) (((node)->u.order << 8) + (port).order)
+
+/*
+ * defines ND_sortweight of each node in r0 w.r.t. r1
+ * returns...
+ */
+static boolean medians(graph_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)) {
+ list_extent = GD_maxinoutdeg(g);
+ 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 = 0; i < GD_rank(g)[r0].n; 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 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[GD_rank(g)[r].n];
+
+ for (i = 0; i < GD_rank(g)[r].n; i++) {
+ lp = &vlist[0];
+ /* 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 savebest(graph_t *g)
+{
+ int nc;
+ Agnode_t *n;
+
+ nc = crossings(g);
+ if (nc < GD_bestcrossings(g)) {
+ for (n = agfstnode(g); n; n = agnxtnode(g,n))
+ ND_saveorder(n) = ND_order(n);
+ GD_bestcrossings(g) = nc;
+ GD_lastwin(g) = GD_pass(g);
+ }
+}
+
+static int ND_order_cmpf(const void *arg0, const void *arg1)
+{
+ return ND_order(*(Agnode_t**)arg0) - ND_order(*(Agnode_t**)arg1);
+}
+
+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 int crossings(graph_t *g)
+{
+ int i, rv;
+
+ rv = 0;
+ for (i = GD_minrank(g); i < GD_maxrank(g); i++) {
+ rv += crossings_below(g,i);
+ }
+ return rv;
+}
+
+/* returns level of (model) node's cluster */
+static int lev(Agnode_t *n)
+{
+ if (!n) return -1;
+ return (GD_level(ND_cluster(n)));
+}
+
+/*
+ * allocates a vpath_t per original user edge. later the path
+ * contents will be set to define vnode chains of intercluster edges.
+ */
+static void interclusterpaths(Agraph_t *ug, Agraph_t *topmodel)
+{
+ Agnode_t *n, *n0;
+ Agedge_t *e, *ue;
+ vpath_t *p;
+
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(ug,n); e; e = agnxtout(ug,e)) {
+ if (ND_cluster(e->tail) != ND_cluster(e->head)) {
+ if (!(p = pathsearch(ug,e->tail,e->head))) {
+ p = newpath(e->tail, e->head);
+ pathinsert(topmodel->root,p);
+ }
+ }
+ }
+ }
+
+ /* scan all vnodes of model graph and make decisions */
+ for (n = agfstnode(topmodel); n; n = agnxtnode(topmodel,n)) {
+ if (ND_type(n) == NODETYPE_VNODE) {
+ ue = ND_erep(n);
+ p = pathsearch(topmodel->root,ue->tail,ue->head);
+ if (p) {
+ if (!(n0 = p->v[ND_rank(n)]) || (lev(n0) < lev(n)))
+ p->v[ND_rank(n)] = n;
+ }
+ }
+ }
+ /* need to remove the other edges of these vnodes!! */
+}
+
+/* build the global (flat) graph of the universe. this is 'flat'
+in the sense that there is one data structure for the entire graph
+(not 'flat' in the sense of flat edges within the same level.)
+*/
+static rank_t *globalize(Agraph_t *user, Agraph_t *topmodel)
+{
+ rank_t *globrank;
+ int minr,maxr,r;
+
+ /* setup bookkeeping */
+ interclusterpaths(user, topmodel);
+
+ /* allocate global ranks */
+ minr = GD_minrank(topmodel);
+ maxr = GD_maxrank(topmodel);
+ globrank = T_array(minr,maxr,sizeof(rank_t));
+ countup(user,globrank);
+ for (r = minr; r <= maxr; r++) {
+ globrank[r].v = N_NEW(globrank[r].n+1,Agnode_t*); /* NIL at end */
+ globrank[r].n = 0; /* reset it */
+ }
+
+ /* installation */
+ for (r = minr; r <= maxr; r++)
+ installglob(user,topmodel,globrank,r);
+
+ removejunk(user, topmodel);
+ reconnect(user, topmodel);
+
+ /* optimization */
+ return globrank;
+}
+
+
+static void countup(Agraph_t *g, rank_t *globr)
+{
+ Agnode_t *n;
+ Agedge_t *e;
+ int r0, r1, low, high, i;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ globr[ND_rank(n)].n += 1;
+ for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
+ r0 = ND_rank(e->tail);
+ r1 = ND_rank(e->head);
+ low = MIN(r0,r1);
+ high = MAX(r0,r1);
+ for (i = low + 1; i < high; i++)
+ globr[i].n += 1;
+ }
+ }
+}
+
+/* install nodes from rank r of (g or its clusters) into globr. */
+static void installglob(Agraph_t *ug, Agraph_t *fromgraph, rank_t *globr, int r)
+{
+ rank_t *myrank;
+ int i;
+ Agnode_t *v;
+ Agedge_t *uedge;
+ vpath_t *path;
+
+ if (is_a_cluster(fromgraph)) {
+ myrank = &GD_rank(fromgraph)[r];
+ i = 0;
+ while ((v = myrank->v[i++])) {
+ switch (ND_type(v)) {
+ case NODETYPE_REAL:
+ /* install primitive nodes */
+ globr[r].v[globr[r].n++] = v;
+ ND_isglobal(v) = TRUE;
+ break;
+ case NODETYPE_VNODE:
+ /* install vnode for non-intercluster edges, and for intercluster
+ edges if this vnode was chosen as the path representative */
+ uedge = ND_erep(v);
+ path = pathsearch(fromgraph->root,uedge->tail,uedge->head);
+ if (!path || (path->v[r] == v)) {
+ globr[r].v[globr[r].n++] = v;
+ ND_isglobal(v) = TRUE;
+ }
+ break;
+ case NODETYPE_CNODE:
+ /* install clusters recursively */
+ installglob(ug,ND_cluster(v),globr,r);
+ break;
+ case NODETYPE_XNODE:
+ /* we're ignoring these */
+ break;
+ }
+ }
+ }
+}
+
+/*
+ after making the global graph, delete all non-global objects.
+ */
+static void removejunk(Agraph_t *ug, Agraph_t *topmodel)
+{
+ Agnode_t *v,*vv;
+
+ for (v = agfstnode(topmodel); v; v = vv) {
+ vv = agnxtnode(topmodel,v);
+ if (!ND_isglobal(v)) agdelete(topmodel,v);
+ }
+}
+
+/*
+ fix up the user edge paths in the global graph.
+*/
+static void reconnect(Agraph_t *ug, Agraph_t *topmodel)
+{
+ Agnode_t *n, *n0, *n1;
+ Agedge_t *e, *e0;
+ vpath_t *p;
+ int i;
+
+ for (n = agfstnode(ug); n; n = agnxtnode(ug,n)) {
+ for (e = agfstout(ug,n); e; e = agnxtout(ug,e)) {
+ if (ND_cluster(e->tail) != ND_cluster(e->head)) {
+ p = pathsearch(ug,e->tail,e->head);
+ n0 = p->v[p->low];
+ for (i = p->low + 1; i <= p->high; i++) {
+ n1 = p->v[i];
+ if (!(e0 = p->e[i])) e0 = agedge(topmodel,n0,n1);
+ ED_weight(e0) += ED_weight(e);
+ ED_xpenalty(e0) += ED_xpenalty(e);
+ n0 = n1;
+ }
+ }
+ }
+ }
+}
+
+#ifdef NOTDEF
+/* (no parallel edges, all forward edges, flat cycles broken) */
+void build_main_graph(Agraph_t *g)
+{
+ Agraph_t *gprime,*gprimeflat;
+ Agnode_t *v,*vprime;
+ Agedge_t *eprev;
+
+ gprime = agopen("model",AGDIGRAPHSTRICT);
+ for (v = agfstnode(g); v; v = agnxtnode(g,v)) {
+ vprime = agnode(gprime,v->name);
+ for (e = agfstout(v); e; e = agxntout(g,e)) {
+ tail = e->tail;
+ head = e->head;
+ if (ND_rank(tail) > ND_rank(head)) {Agnode_t *t = head; head = tail; tail = t;}
+ if (!(eprev = agfindedge(gprime,tail,head))) eprev = agedge(gprime,tail,head);
+ merge(eprev,e);
+ associate(e,eprev);
+ }
+ }
+ break_cycles(gprime);
+}
+#endif
+
+/* I. initialization */
+static void cluster_init(Agraph_t *ug)
+
+{
+ Agraph_t *mg;
+ scangraph(ug);
+ mg = cluster_model(ug);
+ flat_edges(mg);
+ build_ranks(mg,TRUE);
+}
+
+static void rec_cluster_init(Agraph_t *ug)
+{
+ Agraph_t *subg;
+
+ if (is_a_cluster(ug)) cluster_init(ug);
+ for (subg = agfstsubg(ug); subg; subg = agnxtsubg(ug,subg))
+ rec_cluster_init(subg);
+}
--- /dev/null
+#include <vmalloc.h>
+main()
+{
+ Vmalloc_t *region;
+
+ region = vmmopen("/tmp/north123",0,1024*1024);
+ printf("%x\n",malloc(100));
+}
--- /dev/null
+main()
+{
+
+ printf("%x\n",sbrk(0));
+}
--- /dev/null
+#ifndef MSWIN32
+
+//#include <unistd.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/param.h>
+#ifndef HZ
+#define HZ 60
+#endif
+typedef struct tms mytime_t;
+#define GET_TIME(S) times(&(S))
+#define DIFF_IN_SECS(S,T) ((S.tms_utime + S.tms_stime - T.tms_utime - T.tms_stime)/(double)HZ)
+
+#else
+
+#include <time.h>
+typedef clock_t mytime_t;
+#define GET_TIME(S) S = clock()
+#define DIFF_IN_SECS(S,T) ((S - T) / (double)CLOCKS_PER_SEC)
+
+#endif
+
+
+static mytime_t T;
+
+void
+start_timer(void)
+{
+ GET_TIME(T);
+}
+
+double
+elapsed_sec(void)
+{
+ mytime_t S;
+ double rv;
+
+ GET_TIME(S);
+ rv = DIFF_IN_SECS(S,T);
+ return rv;
+}
--- /dev/null
+#include "newdot.h"
+
+typedef struct rsort_s {
+ unsigned char key[5];
+ Agedge_t *e;
+} rsort_t;
+
+main()
+{
+ Agraph_t *g;
+ Agnode_t *n;
+ Agedge_t *e, **edgelist;
+ g = agread(stdin);
+ edgelist = alledges(g);
+ radixsort(edgelist, agnedges(g), 0, endchar);
+}
--- /dev/null
+void dot_X(Agraph_t *ug)
+{
+ graph_t *Xg = agopen(AGSTRICTDIRECTED,"constraints");
+
+ make_nodevars(ug,Xg);
+ make_edgevars(ug,Xg);
+ make_clustvars(ug,Xg);
+ constrain_nodenode(ug,Xg);
+ constrain_edgelen(ug,Xg);
+ constraint_nodeclust(ug,Xg);
+ constraint_clustclust(ug,Xg);
+ solve();
+ read_nodevars(ug,Xg);
+ read_clustvars(ug,Xg);
+}
--- /dev/null
+#include "newdot.h"
+
+static Agraph_t *constraint_graph(Agraph_t *model);
+static void node_separation_constraints(Agraph_t *user);
+static void cluster_containment_constraints(Agraph_t *upar, Agraph_t *ug);
+static void edge_cost_constraints(Agraph_t *user);
+
+void dot_position(Agraph_t *user)
+{
+ Agraph_t *cg;
+ cg = constraint_graph(user);
+ node_separation_constraints(user);
+ cluster_containment_constraints(user);
+ edge_cost_constraints(user);
+ rank(cg);
+ readout(user);
+ agclose(cg);
+}
+
+static Agraph_t *constraint_graph(Agraph_t *model)
+{
+ if (!GD_model(model->root))
+ GD_model(model->root) = agopen("Xconstraint",AGDIGRAPHSTRICT);
+ return GD_model(model->root);
+}
+
+static void node_sep(Agraph_t *g, Agnode_t *left, Agnode_t *right)
+{
+ Agraph_t *Xg;
+ Agnode_t_ *leftvar; *rightvar;
+ double d0, d1;
+
+ d0 = ND_xsize(left) / 2.0;
+ d1 = ND_xsize(right) / 2.0;
+ /* what about self edge sizes */
+ Xg = constraint_graph(g);
+ leftvar = nodevar(left);
+ rightvar = nodevar(right);
+ e = agedge(Xg, leftvar, rightvar);
+ ED_weight(e) = 0;
+ ED_minlen(e) = XSCALE(d0 + d1);
+}
+
+/* g is a model graph */
+static void node_separation_constraints(Agraph_t *user)
+{
+ Agraph_t *g;
+ rank_t *r;
+
+ g = GD_model(user);
+ for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
+ r = GD_rank(g)[i];
+ left = NILnode;
+ for (j = leftmost(g,r); j <= rightmost(g,r); j++) {
+ right = r->v[j];
+ node_sep(g,left,right);
+ right = left;
+ }
+ }
+}
+
+/*
+ * this function has to traverse the subgraph hierarchy from the
+ * user graph because we flattened out the model subgraphs.
+ */
+static void cluster_containment_constraints(Agraph_t *upar, Agraph_t *ug)
+{
+ Agraph_t *container;
+
+ if (is_a_cluster(upar) && is_a_cluster(ug))
+ cluster_contain(upar,ug);
+ container = is_a_cluster(ug)? ug : upar;
+ for (subg = agfstsub(ug); subg; subg = agnxtsubg(ug,subg))
+ cluster_containment_constraints(container,subg);
+}
+
+static void cluster_contain(Agraph_t *u_outer, Agraph_t *u_inner)
+{
+ Agraph_t *Xg;
+ int sep;
+ Agnode_t *outer_left, *outer_right, *inner_left, *inner_right;
+
+ sep = user_cluster_sep(u_inner);
+ Xg = constraint_graph(GD_model(u_outer));
+ outer_left = leftvar(GD_model(u_outer));
+ outer_right = rightvar(GD_model(u_outer));
+ inner_left = leftvar(GD_model(u_inner));
+ inner_right = rightvar(GD_model(u_inner));
+ clust_sep_edge(outer_left,inner_left);
+ clust_sep_edge(inner_right,outer_right);
+}
+
+static void clust_sep_edge(Agraph_t *Xg, Agnode_t *left, Agnode_t *right, int sep)
+{
+ Agedge_t *e;
+
+ e = agedge(Xg, left, right);
+ ED_weight(e) = 0;
+ ED_minlen(e) = sep;
+}
+
+static int vedge_cost(int r, int low, int high)
+{
+ if (low + 1 == high) return SHORT_FACTOR;
+ if ((r == low) || (r == high)) return LONG_END_FACTOR;
+ return LONG_FACTOR;
+}
+
+static void edge_cost_constraints(Agraph_t *user)
+{
+ Agraph_t *model;
+ Agraph_t *Xg; /* constraints */
+ Dict_t *dict; /* set of paths in the model graph */
+ vpath_t *p;
+
+ model = GD_model(user);
+ Xg = constraint_graph(model);
+ dict = GD_pathdict(model);
+ for (p = dtfirst(dict); p; p = dtnext(dict,p)) {
+ v0 = nodevar(p->v[p->low]);
+ for (i = p->low; i < p->high; i++) {
+ v1 = nodevar(p->v[i+1]);
+ vx = agnode(Xg,0);
+ e0 = agedge(Xg,vx,v0);
+ e1 = agedge(Xg,vx,v1);
+ ED_weight(e0) = ED_weight(e1) = p->weight * vedge_factor(i,low,high);
+
+ tp = (i == p->low?) p->tailport : 0;
+ hp = (i == p->high - 1?) p->headport : 0;
+ m0 = hp - tp;
+ if (m0 > 0) ED_minlen(e0) = m0;
+ else ED_minlen(e1) = -m0;
+ }
+ }
+}