1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 
15 /*
16  * Rank the nodes of a directed graph, subject to user-defined
17  * sets of nodes to be kept on the same, min, or max rank.
18  * The temporary acyclic fast graph is constructed and ranked
19  * by a network-simplex technique.  Then ranks are propagated
20  * to non-leader nodes and temporary edges are deleted.
21  * Leaf nodes and top-level clusters are left collapsed, though.
22  * Assigns global minrank and maxrank of graph and all clusters.
23  *
24  * TODO: safety code.  must not be in two clusters at same level.
25  *  must not be in same/min/max/rank and a cluster at the same time.
26  *  watch out for interactions between leaves and clusters.
27  */
28 
29 #include	"dot.h"
30 
31 static void dot1_rank(graph_t * g, aspect_t* asp);
32 static void dot2_rank(graph_t * g, aspect_t* asp);
33 
34 static void
renewlist(elist * L)35 renewlist(elist * L)
36 {
37     int i;
38     for (i = L->size; i >= 0; i--)
39 	L->list[i] = NULL;
40     L->size = 0;
41 }
42 
43 static void
cleanup1(graph_t * g)44 cleanup1(graph_t * g)
45 {
46     node_t *n;
47     edge_t *e, *f;
48     int c;
49 
50     for (c = 0; c < GD_comp(g).size; c++) {
51 	    GD_nlist(g) = GD_comp(g).list[c];
52 	    for (n = GD_nlist(g); n; n = ND_next(n)) {
53 	        renewlist(&ND_in(n));
54 	        renewlist(&ND_out(n));
55 	        ND_mark(n) = FALSE;
56 	    }
57     }
58     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
59         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
60             f = ED_to_virt(e);
61             /* Null out any other references to f to make sure we don't
62              * handle it a second time. For example, parallel multiedges
63              * share a virtual edge.
64              */
65             if (f && (e != ED_to_orig(f))) {
66                 ED_to_virt(e) = NULL;
67             }
68         }
69     }
70     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
71         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
72             f = ED_to_virt(e);
73             if (f && ED_to_orig(f) == e) {
74 		        free(f->base.data);
75 		        free(f);
76                 ED_to_virt(e) = NULL;
77             }
78 	    }
79     }
80     free(GD_comp(g).list);
81     GD_comp(g).list = NULL;
82     GD_comp(g).size = 0;
83 }
84 
85 /*static void
86 cleanup1(graph_t * g)
87 {
88     node_t *n;
89     edge_t *e, *f;
90     int c;
91 
92     for (c = 0; c < GD_comp(g).size; c++) {
93         GD_nlist(g) = GD_comp(g).list[c];
94         for (n = GD_nlist(g); n; n = ND_next(n)) {
95             renewlist(&ND_in(n));
96             renewlist(&ND_out(n));
97             ND_mark(n) = FALSE;
98         }
99     }
100     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
101         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
102             f = ED_to_virt(e);
103             * Null out any other references to f to make sure we don't
104              * handle it a second time. For example, parallel multiedges
105              * share a virtual edge.
106              *
107             if (f && (e == ED_to_orig(f))) {
108                 edge_t *e1, *f1;
109                 node_t *n1;
110                 for (n1 = agfstnode(g); n1; n1 = agnxtnode(g, n1)) {
111                     for (e1 = agfstout(g, n1); e1; e1 = agnxtout(g, e1)) {
112                         if (e != e1) {
113                             f1 = ED_to_virt(e1);
114                             if (f1 && (f == f1)) {
115                                 ED_to_virt(e1) = NULL;
116                             }
117                         }
118                     }
119                 }
120                 free(f->base.data);
121                 free(f);
122             }
123             ED_to_virt(e) = NULL;
124         }
125     }
126     free(GD_comp(g).list);
127     GD_comp(g).list = NULL;
128     GD_comp(g).size = 0;
129 }
130 */
131 
132 /* When there are edge labels, extra ranks are reserved here for the virtual
133  * nodes of the labels.  This is done by doubling the input edge lengths.
134  * The input rank separation is adjusted to compensate.
135  */
136 static void
edgelabel_ranks(graph_t * g)137 edgelabel_ranks(graph_t * g)
138 {
139     node_t *n;
140     edge_t *e;
141 
142     if (GD_has_labels(g) & EDGE_LABEL) {
143 	for (n = agfstnode(g); n; n = agnxtnode(g, n))
144 	    for (e = agfstout(g, n); e; e = agnxtout(g, e))
145 		ED_minlen(e) *= 2;
146 	GD_ranksep(g) = (GD_ranksep(g) + 1) / 2;
147     }
148 }
149 
150 /* Merge the nodes of a min, max, or same rank set. */
151 static void
collapse_rankset(graph_t * g,graph_t * subg,int kind)152 collapse_rankset(graph_t * g, graph_t * subg, int kind)
153 {
154     node_t *u, *v;
155 
156     u = v = agfstnode(subg);
157     if (u) {
158 	ND_ranktype(u) = kind;
159 	while ((v = agnxtnode(subg, v))) {
160 	    UF_union(u, v);
161 	    ND_ranktype(v) = ND_ranktype(u);
162 	}
163 	switch (kind) {
164 	case MINRANK:
165 	case SOURCERANK:
166 	    if (GD_minset(g) == NULL)
167 		GD_minset(g) = u;
168 	    else
169 		GD_minset(g) = UF_union(GD_minset(g), u);
170 	    break;
171 	case MAXRANK:
172 	case SINKRANK:
173 	    if (GD_maxset(g) == NULL)
174 		GD_maxset(g) = u;
175 	    else
176 		GD_maxset(g) = UF_union(GD_maxset(g), u);
177 	    break;
178 	}
179 	switch (kind) {
180 	case SOURCERANK:
181 	    ND_ranktype(GD_minset(g)) = kind;
182 	    break;
183 	case SINKRANK:
184 	    ND_ranktype(GD_maxset(g)) = kind;
185 	    break;
186 	}
187     }
188 }
189 
190 static int
rank_set_class(graph_t * g)191 rank_set_class(graph_t * g)
192 {
193     static char *name[] = { "same", "min", "source", "max", "sink", NULL };
194     static int class[] =
195 	{ SAMERANK, MINRANK, SOURCERANK, MAXRANK, SINKRANK, 0 };
196     int val;
197 
198     if (is_cluster(g))
199 	return CLUSTER;
200     val = maptoken(agget(g, "rank"), name, class);
201     GD_set_type(g) = val;
202     return val;
203 }
204 
205 static int
make_new_cluster(graph_t * g,graph_t * subg)206 make_new_cluster(graph_t * g, graph_t * subg)
207 {
208     int cno;
209     cno = ++(GD_n_cluster(g));
210     GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g));
211     GD_clust(g)[cno] = subg;
212     do_graph_label(subg);
213     return cno;
214 }
215 
216 static void
node_induce(graph_t * par,graph_t * g)217 node_induce(graph_t * par, graph_t * g)
218 {
219     node_t *n, *nn;
220     edge_t *e;
221     int i;
222 
223     /* enforce that a node is in at most one cluster at this level */
224     for (n = agfstnode(g); n; n = nn) {
225 	nn = agnxtnode(g, n);
226 	if (ND_ranktype(n)) {
227 	    agdelete(g, n);
228 	    continue;
229 	}
230 	for (i = 1; i < GD_n_cluster(par); i++)
231 	    if (agcontains(GD_clust(par)[i], n))
232 		break;
233 	if (i < GD_n_cluster(par))
234 	    agdelete(g, n);
235 	ND_clust(n) = NULL;
236     }
237 
238     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
239 	for (e = agfstout(dot_root(g), n); e; e = agnxtout(dot_root(g), e)) {
240 	    if (agcontains(g, aghead(e)))
241 		agsubedge(g,e,1);
242 	}
243     }
244 }
245 
246 void
dot_scan_ranks(graph_t * g)247 dot_scan_ranks(graph_t * g)
248 {
249     node_t *n, *leader = NULL;
250     GD_minrank(g) = MAXSHORT;
251     GD_maxrank(g) = -1;
252     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
253 	if (GD_maxrank(g) < ND_rank(n))
254 	    GD_maxrank(g) = ND_rank(n);
255 	if (GD_minrank(g) > ND_rank(n))
256 	    GD_minrank(g) = ND_rank(n);
257 	if (leader == NULL)
258 	    leader = n;
259 	else {
260 	    if (ND_rank(n) < ND_rank(leader))
261 		leader = n;
262 	}
263     }
264     GD_leader(g) = leader;
265 }
266 
267 static void
cluster_leader(graph_t * clust)268 cluster_leader(graph_t * clust)
269 {
270     node_t *leader, *n;
271     int maxrank = 0;
272 
273     /* find number of ranks and select a leader */
274     leader = NULL;
275     for (n = GD_nlist(clust); n; n = ND_next(n)) {
276 	if ((ND_rank(n) == 0) && (ND_node_type(n) == NORMAL))
277 	    leader = n;
278 	if (maxrank < ND_rank(n))
279 	    maxrank = ND_rank(n);
280     }
281     assert(leader != NULL);
282     GD_leader(clust) = leader;
283 
284     for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) {
285 	assert((ND_UF_size(n) <= 1) || (n == leader));
286 	UF_union(n, leader);
287 	ND_ranktype(n) = CLUSTER;
288     }
289 }
290 
291 /*
292  * A cluster is collapsed in three steps.
293  * 1) The nodes of the cluster are ranked locally.
294  * 2) The cluster is collapsed into one node on the least rank.
295  * 3) In class1(), any inter-cluster edges are converted using
296  *    the "virtual node + 2 edges" trick.
297  */
298 static void
collapse_cluster(graph_t * g,graph_t * subg)299 collapse_cluster(graph_t * g, graph_t * subg)
300 {
301     if (GD_parent(subg)) {
302 	return;
303     }
304     GD_parent(subg) = g;
305     node_induce(g, subg);
306     if (agfstnode(subg) == NULL)
307 	return;
308     make_new_cluster(g, subg);
309     if (CL_type == LOCAL) {
310 	dot1_rank(subg, 0);
311 	cluster_leader(subg);
312     } else
313 	dot_scan_ranks(subg);
314 }
315 
316 /* Execute union commands for "same rank" subgraphs and clusters. */
317 static void
collapse_sets(graph_t * rg,graph_t * g)318 collapse_sets(graph_t *rg, graph_t *g)
319 {
320     int c;
321     graph_t  *subg;
322 #ifdef OBSOLETE
323     node_t *n;
324 #endif
325 
326     for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
327 	c = rank_set_class(subg);
328 	if (c) {
329 	    if ((c == CLUSTER) && CL_type == LOCAL)
330 		collapse_cluster(rg, subg);
331 	    else
332 		collapse_rankset(rg, subg, c);
333 	}
334 	else collapse_sets(rg, subg);
335 
336 #ifdef OBSOLETE
337  Collapsing leaves is currently obsolete
338 
339 	/* mark nodes with ordered edges so their leaves are not collapsed */
340 	if (agget(subg, "ordering"))
341 	    for (n = agfstnode(subg); n; n = agnxtnode(subg, n))
342 		ND_order(n) = 1;
343 #endif
344     }
345 }
346 
347 static void
find_clusters(graph_t * g)348 find_clusters(graph_t * g)
349 {
350     graph_t *subg;
351     for (subg = agfstsubg(dot_root(g)); subg; subg = agnxtsubg(subg)) {
352 	if (GD_set_type(subg) == CLUSTER)
353 	    collapse_cluster(g, subg);
354     }
355 }
356 
357 static void
set_minmax(graph_t * g)358 set_minmax(graph_t * g)
359 {
360     int c;
361 
362     GD_minrank(g) += ND_rank(GD_leader(g));
363     GD_maxrank(g) += ND_rank(GD_leader(g));
364     for (c = 1; c <= GD_n_cluster(g); c++)
365 	set_minmax(GD_clust(g)[c]);
366 }
367 
368 /* To ensure that min and max rank nodes always have the intended rank
369  * assignment, reverse any incompatible edges.
370  */
371 static point
minmax_edges(graph_t * g)372 minmax_edges(graph_t * g)
373 {
374     node_t *n;
375     edge_t *e;
376     point  slen;
377 
378     slen.x = slen.y = 0;
379     if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL))
380 	return slen;
381     if (GD_minset(g) != NULL)
382 	GD_minset(g) = UF_find(GD_minset(g));
383     if (GD_maxset(g) != NULL)
384 	GD_maxset(g) = UF_find(GD_maxset(g));
385 
386     if ((n = GD_maxset(g))) {
387 	slen.y = (ND_ranktype(GD_maxset(g)) == SINKRANK);
388 	while ((e = ND_out(n).list[0])) {
389 	    assert(aghead(e) == UF_find(aghead(e)));
390 	    reverse_edge(e);
391 	}
392     }
393     if ((n = GD_minset(g))) {
394 	slen.x = (ND_ranktype(GD_minset(g)) == SOURCERANK);
395 	while ((e = ND_in(n).list[0])) {
396 	    assert(agtail(e) == UF_find(agtail(e)));
397 	    reverse_edge(e);
398 	}
399     }
400     return slen;
401 }
402 
403 static int
minmax_edges2(graph_t * g,point slen)404 minmax_edges2(graph_t * g, point slen)
405 {
406     node_t *n;
407     edge_t *e = 0;
408 
409     if ((GD_maxset(g)) || (GD_minset(g))) {
410 	for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
411 	    if (n != UF_find(n))
412 		continue;
413 	    if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) {
414 		e = virtual_edge(n, GD_maxset(g), NULL);
415 		ED_minlen(e) = slen.y;
416 		ED_weight(e) = 0;
417 	    }
418 	    if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) {
419 		e = virtual_edge(GD_minset(g), n, NULL);
420 		ED_minlen(e) = slen.x;
421 		ED_weight(e) = 0;
422 	    }
423 	}
424     }
425     return (e != 0);
426 }
427 
428 /* Run the network simplex algorithm on each component. */
rank1(graph_t * g)429 void rank1(graph_t * g)
430 {
431     int maxiter = INT_MAX;
432     int c;
433     char *s;
434 
435     if ((s = agget(g, "nslimit1")))
436 	maxiter = atof(s) * agnnodes(g);
437     for (c = 0; c < GD_comp(g).size; c++) {
438 	GD_nlist(g) = GD_comp(g).list[c];
439 	rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter);	/* TB balance */
440     }
441 }
442 
443 /*
444  * Assigns ranks of non-leader nodes.
445  * Expands same, min, max rank sets.
446  * Leaf sets and clusters remain merged.
447  * Sets minrank and maxrank appropriately.
448  */
expand_ranksets(graph_t * g,aspect_t * asp)449 static void expand_ranksets(graph_t * g, aspect_t* asp)
450 {
451     int c;
452     node_t *n, *leader;
453 
454     if ((n = agfstnode(g))) {
455 	GD_minrank(g) = MAXSHORT;
456 	GD_maxrank(g) = -1;
457 	while (n) {
458 	    leader = UF_find(n);
459 	    /* The following works because ND_rank(n) == 0 if n is not in a
460 	     * cluster, and ND_rank(n) = the local rank offset if n is in
461 	     * a cluster. */
462 	    if ((leader != n) && (!asp || (ND_rank(n) == 0)))
463 		ND_rank(n) += ND_rank(leader);
464 
465 	    if (GD_maxrank(g) < ND_rank(n))
466 		GD_maxrank(g) = ND_rank(n);
467 	    if (GD_minrank(g) > ND_rank(n))
468 		GD_minrank(g) = ND_rank(n);
469 
470 	    if (ND_ranktype(n) && (ND_ranktype(n) != LEAFSET))
471 		UF_singleton(n);
472 	    n = agnxtnode(g, n);
473 	}
474 	if (g == dot_root(g)) {
475 	    if (CL_type == LOCAL) {
476 		for (c = 1; c <= GD_n_cluster(g); c++)
477 		    set_minmax(GD_clust(g)[c]);
478 	    } else {
479 		find_clusters(g);
480 	    }
481 	}
482     } else {
483 	GD_minrank(g) = GD_maxrank(g) = 0;
484     }
485 }
486 
487 #ifdef ALLOW_LEVELS
488 void
setRanks(graph_t * g,attrsym_t * lsym)489 setRanks (graph_t* g, attrsym_t* lsym)
490 {
491     node_t* n;
492     char*   s;
493     char*   ep;
494     long    v;
495 
496     for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
497 	s = agxget (n, lsym);
498 	v = strtol (s, &ep, 10);
499 	if (ep == s)
500 	    agerr(AGWARN, "no level attribute for node \"%s\"\n", agnameof(n));
501 	ND_rank(n) = v;
502     }
503 }
504 #endif
505 
506 #ifdef UNUSED
507 static node_t **virtualEdgeHeadList = NULL;
508 static node_t **virtualEdgeTailList = NULL;
509 static int nVirtualEdges = 0;
510 
511 static void
saveVirtualEdges(graph_t * g)512 saveVirtualEdges(graph_t *g)
513 {
514     edge_t *e;
515     node_t *n;
516     int cnt = 0;
517     int lc;
518 
519     if (virtualEdgeHeadList != NULL) {
520 	free(virtualEdgeHeadList);
521     }
522     if (virtualEdgeTailList != NULL) {
523 	free(virtualEdgeTailList);
524     }
525 
526   /* allocate memory */
527     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
528 	for (lc = 0; lc < ND_in(n).size; lc++) {
529 	    e = ND_in(n).list[lc];
530 	    if (ED_edge_type(e) == VIRTUAL) cnt++;
531 	}
532     }
533 
534     nVirtualEdges = cnt;
535     virtualEdgeHeadList = N_GNEW(cnt, node_t*);
536     virtualEdgeTailList = N_GNEW(cnt, node_t*);
537 
538     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
539 	for (lc = 0, cnt = 0; lc < ND_in(n).size; lc++) {
540 	    e = ND_in(n).list[lc];
541 	    if (ED_edge_type(e) == VIRTUAL) {
542 		virtualEdgeHeadList[cnt] = e->head;
543 		virtualEdgeTailList[cnt] = e->tail;
544 		if (Verbose)
545 		    printf("saved virtual edge: %s->%s\n",
546 			virtualEdgeTailList[cnt]->name,
547 			virtualEdgeHeadList[cnt]->name);
548 		cnt++;
549 	    }
550 	}
551     }
552 }
553 
554 static void
restoreVirtualEdges(graph_t * g)555 restoreVirtualEdges(graph_t *g)
556 {
557     int i;
558     edge_t e;
559 
560     for (i = 0; i < nVirtualEdges; i++) {
561 	if (virtualEdgeTailList[i] && virtualEdgeHeadList[i]) {
562 	    if (Verbose)
563 		printf("restoring virtual edge: %s->%s\n",
564 		    virtualEdgeTailList[i]->name, virtualEdgeHeadList[i]->name);
565 	    virtual_edge(virtualEdgeTailList[i], virtualEdgeHeadList[i], NULL);
566 	}
567     }
568     if (Verbose)
569 	printf("restored %d virt edges\n", nVirtualEdges);
570 }
571 #endif
572 
573 /* dot1_rank:
574  * asp != NULL => g is root
575  */
dot1_rank(graph_t * g,aspect_t * asp)576 static void dot1_rank(graph_t * g, aspect_t* asp)
577 {
578     point p;
579 #ifdef ALLOW_LEVELS
580     attrsym_t* N_level;
581 #endif
582     edgelabel_ranks(g);
583 
584     if (asp) {
585 	init_UF_size(g);
586 	initEdgeTypes(g);
587     }
588 
589     collapse_sets(g,g);
590     /*collapse_leaves(g); */
591     class1(g);
592     p = minmax_edges(g);
593     decompose(g, 0);
594     if (asp && ((GD_comp(g).size > 1)||(GD_n_cluster(g) > 0))) {
595 	asp->badGraph = 1;
596 	asp = NULL;
597     }
598     acyclic(g);
599     if (minmax_edges2(g, p))
600 	decompose(g, 0);
601 #ifdef ALLOW_LEVELS
602     if ((N_level = agattr(g,AGNODE,"level",NULL)))
603 	setRanks(g, N_level);
604     else
605 #endif
606 
607     if (asp)
608 	rank3(g, asp);
609     else
610 	rank1(g);
611 
612     expand_ranksets(g, asp);
613     cleanup1(g);
614 }
615 
dot_rank(graph_t * g,aspect_t * asp)616 void dot_rank(graph_t * g, aspect_t* asp)
617 {
618     if (agget (g, "newrank")) {
619 	GD_flags(g) |= NEW_RANK;
620 	dot2_rank (g, asp);
621     }
622     else
623 	dot1_rank (g, asp);
624     if (Verbose)
625 	fprintf (stderr, "Maxrank = %d, minrank = %d\n", GD_maxrank(g), GD_minrank(g));
626 }
627 
is_cluster(graph_t * g)628 int is_cluster(graph_t * g)
629 {
630     //return (strncmp(agnameof(g), "cluster", 7) == 0);
631     return is_a_cluster(g);   // from utils.c
632 }
633 
634 #ifdef OBSOLETE
635 static node_t*
merge_leaves(graph_t * g,node_t * cur,node_t * new)636 merge_leaves(graph_t * g, node_t * cur, node_t * new)
637 {
638     node_t *rv;
639 
640     if (cur == NULL)
641 	rv = new;
642     else {
643 	rv = UF_union(cur, new);
644 	ND_ht(rv) = MAX(ND_ht(cur), ND_ht(new));
645 	ND_lw(rv) = ND_lw(cur) + ND_lw(new) + GD_nodesep(g) / 2;
646 	ND_rw(rv) = ND_rw(cur) + ND_rw(new) + GD_nodesep(g) / 2;
647     }
648     return rv;
649 }
650 
651 static void
potential_leaf(graph_t * g,edge_t * e,node_t * leaf)652 potential_leaf(graph_t * g, edge_t * e, node_t * leaf)
653 {
654     node_t *par;
655 
656     if ((ED_tail_port(e).p.x) || (ED_head_port(e).p.x))
657 	return;
658     if ((ED_minlen(e) != 1) || (ND_order(agtail(e)) > 0))
659 	return;
660     par = ((leaf != aghead(e)) ? aghead(e) : agtail(e));
661     ND_ranktype(leaf) = LEAFSET;
662     if (par == agtail(e))
663 	GD_outleaf(par) = merge_leaves(g, GD_outleaf(par), leaf);
664     else
665 	GD_inleaf(par) = merge_leaves(g, GD_inleaf(par), leaf);
666 }
667 
668 static void
collapse_leaves(graph_t * g)669 collapse_leaves(graph_t * g)
670 {
671     node_t *n;
672     edge_t *e;
673 
674     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
675 
676 	/* consider n as a potential leaf of some other node. */
677 	if ((ND_ranktype(n) != NOCMD) || (ND_order(n)))
678 	    continue;
679 	if (agfstout(g, n) == NULL) {
680 	    if ((e = agfstin(g, n)) && (agnxtin(g, e) == NULL)) {
681 		potential_leaf(g, AGMKOUT(e), n);
682 		continue;
683 	    }
684 	}
685 	if (agfstin(g, n) == NULL) {
686 	    if ((e = agfstout(g, n)) && (agnxtout(g, e) == NULL)) {
687 		potential_leaf(g, e, n);
688 		continue;
689 	    }
690 	}
691     }
692 }
693 #endif
694 
695 /* new ranking code:
696  * Allows more constraints
697  * Copy of level.c in dotgen2
698  * Many of the utility functions are simpler or gone with
699  * cgraph library.
700  */
701 #define	BACKWARD_PENALTY	1000
702 #define STRONG_CLUSTER_WEIGHT   1000
703 #define	NORANK		6
704 #define ROOT    "\177root"
705 #define TOPNODE     "\177top"
706 #define BOTNODE     "\177bot"
707 
708 /* hops is not used in dot, so we overload it to
709  * contain the index of the connected component
710  */
711 #define ND_comp(n)  ND_hops(n)
712 
713 extern int rank2(Agraph_t *, int, int, int);
714 
set_parent(graph_t * g,graph_t * p)715 static void set_parent(graph_t* g, graph_t* p)
716 {
717     GD_parent(g) = p;
718     make_new_cluster(p, g);
719     node_induce(p, g);
720 }
721 
is_empty(graph_t * g)722 static int is_empty(graph_t * g)
723 {
724     return (!agfstnode(g));
725 }
726 
is_a_strong_cluster(graph_t * g)727 static int is_a_strong_cluster(graph_t * g)
728 {
729     int rv;
730     char *str = agget(g, "compact");
731     /* rv = mapBool((str), TRUE); */
732     rv = mapBool((str), FALSE);
733     return rv;
734 }
735 
rankset_kind(graph_t * g)736 static int rankset_kind(graph_t * g)
737 {
738     char *str = agget(g, "rank");
739 
740     if (str && str[0]) {
741 	if (!strcmp(str, "min"))
742 	    return MINRANK;
743 	if (!strcmp(str, "source"))
744 	    return SOURCERANK;
745 	if (!strcmp(str, "max"))
746 	    return MAXRANK;
747 	if (!strcmp(str, "sink"))
748 	    return SINKRANK;
749 	if (!strcmp(str, "same"))
750 	    return SAMERANK;
751     }
752     return NORANK;
753 }
754 
is_nonconstraint(edge_t * e)755 static int is_nonconstraint(edge_t * e)
756 {
757     char *constr;
758 
759     if (E_constr && (constr = agxget(e, E_constr))) {
760 	if (constr[0] && mapbool(constr) == FALSE)
761 	    return TRUE;
762     }
763     return FALSE;
764 }
765 
find(node_t * n)766 static node_t *find(node_t * n)
767 {
768     node_t *set;
769     if ((set = ND_set(n))) {
770 	if (set != n)
771 	    set = ND_set(n) = find(set);
772     } else
773 	set = ND_set(n) = n;
774     return set;
775 }
776 
union_one(node_t * leader,node_t * n)777 static node_t *union_one(node_t * leader, node_t * n)
778 {
779     if (n)
780 	return (ND_set(find(n)) = find(leader));
781     else
782 	return leader;
783 }
784 
union_all(graph_t * g)785 static node_t *union_all(graph_t * g)
786 {
787     node_t *n, *leader;
788 
789     n = agfstnode(g);
790     if (!n)
791 	return n;
792     leader = find(n);
793     while ((n = agnxtnode(g, n)))
794 	union_one(leader, n);
795     return leader;
796 }
797 
compile_samerank(graph_t * ug,graph_t * parent_clust)798 static void compile_samerank(graph_t * ug, graph_t * parent_clust)
799 {
800     graph_t *s;			/* subgraph being scanned */
801     graph_t *clust;		/* cluster that contains the rankset */
802     node_t *n, *leader;
803 
804     if (is_empty(ug))
805 	return;
806     if (is_a_cluster(ug)) {
807 	clust = ug;
808 	if (parent_clust) {
809 	    GD_level(ug) = GD_level(parent_clust) + 1;
810 	    set_parent(ug, parent_clust);
811 	}
812 	else
813 	    GD_level(ug) = 0;
814     } else
815 	clust = parent_clust;
816 
817     /* process subgraphs of this subgraph */
818     for (s = agfstsubg(ug); s; s = agnxtsubg(s))
819 	compile_samerank(s, clust);
820 
821     /* process this subgraph as a cluster */
822     if (is_a_cluster(ug)) {
823 	for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
824 	    if (ND_clust(n) == 0)
825 		ND_clust(n) = ug;
826 #ifdef DEBUG
827 	    fprintf(stderr, "(%s) %s  %p\n", agnameof(ug), agnameof(n),
828 		    ND_clust(n));
829 #endif
830 	}
831     }
832 
833     /* process this subgraph as a rankset */
834     switch (rankset_kind(ug)) {
835     case SOURCERANK:
836 	GD_has_sourcerank(clust) = TRUE;	/* fall through */
837     case MINRANK:
838 	leader = union_all(ug);
839 	GD_minrep(clust) = union_one(leader, GD_minrep(clust));
840 	break;
841     case SINKRANK:
842 	GD_has_sinkrank(clust) = TRUE;	/* fall through */
843     case MAXRANK:
844 	leader = union_all(ug);
845 	GD_maxrep(clust) = union_one(leader, GD_maxrep(clust));
846 	break;
847     case SAMERANK:
848 	leader = union_all(ug);
849 	/* do we need to record these ranksets? */
850 	break;
851     case NORANK:
852 	break;
853     default:			/* unrecognized - warn and do nothing */
854 	agerr(AGWARN, "%s has unrecognized rank=%s", agnameof(ug),
855 	      agget(ug, "rank"));
856     }
857 
858     /* a cluster may become degenerate */
859     if (is_a_cluster(ug) && GD_minrep(ug)) {
860 	if (GD_minrep(ug) == GD_maxrep(ug)) {
861 	    node_t *up = union_all(ug);
862 	    GD_minrep(ug) = up;
863 	    GD_maxrep(ug) = up;
864 	}
865     }
866 }
867 
dot_lca(graph_t * c0,graph_t * c1)868 static graph_t *dot_lca(graph_t * c0, graph_t * c1)
869 {
870     while (c0 != c1) {
871 	if (GD_level(c0) >= GD_level(c1))
872 	    c0 = GD_parent(c0);
873 	else
874 	    c1 = GD_parent(c1);
875     }
876     return c0;
877 }
878 
is_internal_to_cluster(edge_t * e)879 static int is_internal_to_cluster(edge_t * e)
880 {
881     graph_t *par, *ct, *ch;
882     ct = ND_clust(agtail(e));
883     ch = ND_clust(aghead(e));
884     if (ct == ch)
885 	return TRUE;
886     par = dot_lca(ct, ch);
887     /* if (par == agroot(par)) */
888 	/* return FALSE; */
889     if ((par == ct) || (par == ch))
890 	return TRUE;
891     return FALSE;
892 }
893 
894 static node_t* Last_node;
makeXnode(graph_t * G,char * name)895 static node_t* makeXnode (graph_t* G, char* name)
896 {
897     node_t *n = agnode(G, name, 1);
898     alloc_elist(4, ND_in(n));
899     alloc_elist(4, ND_out(n));
900     if (Last_node) {
901 	ND_prev(n) = Last_node;
902 	ND_next(Last_node) = n;
903     } else {
904 	ND_prev(n) = NULL;
905 	GD_nlist(G) = n;
906     }
907     Last_node = n;
908     ND_next(n) = NULL;
909 
910     return n;
911 }
912 
compile_nodes(graph_t * g,graph_t * Xg)913 static void compile_nodes(graph_t * g, graph_t * Xg)
914 {
915     /* build variables */
916     node_t *n;
917 
918     Last_node = NULL;
919     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
920 	if (find(n) == n)
921 	    ND_rep(n) = makeXnode (Xg, agnameof(n));
922     }
923     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
924 	if (ND_rep(n) == 0)
925 	    ND_rep(n) = ND_rep(find(n));
926     }
927 }
928 
merge(edge_t * e,int minlen,int weight)929 static void merge(edge_t * e, int minlen, int weight)
930 {
931     ED_minlen(e) = MAX(ED_minlen(e), minlen);
932     ED_weight(e) += weight;
933 }
934 
strong(graph_t * g,node_t * t,node_t * h,edge_t * orig)935 static void strong(graph_t * g, node_t * t, node_t * h, edge_t * orig)
936 {
937     edge_t *e;
938     if ((e = agfindedge(g, t, h)) ||
939 	(e = agfindedge(g, h, t)) || (e = agedge(g, t, h, 0, 1)))
940 	merge(e, ED_minlen(orig), ED_weight(orig));
941     else
942 	agerr(AGERR, "ranking: failure to create strong constraint edge between nodes %s and %s\n",
943 	    agnameof(t), agnameof(h));
944 }
945 
weak(graph_t * g,node_t * t,node_t * h,edge_t * orig)946 static void weak(graph_t * g, node_t * t, node_t * h, edge_t * orig)
947 {
948     node_t *v;
949     edge_t *e, *f;
950     static int id;
951     char buf[100];
952 
953     for (e = agfstin(g, t); e; e = agnxtin(g, e)) {
954 	/* merge with existing weak edge (e,f) */
955 	v = agtail(e);
956 	if ((f = agfstout(g, v)) && (aghead(f) == h)) {
957 	    return;
958 	}
959     }
960     if (!e) {
961 	sprintf (buf, "_weak_%d", id++);
962 	v = makeXnode(g, buf);
963 	e = agedge(g, v, t, 0, 1);
964 	f = agedge(g, v, h, 0, 1);
965     }
966     ED_minlen(e) = MAX(ED_minlen(e), 0);	/* effectively a nop */
967     ED_weight(e) += ED_weight(orig) * BACKWARD_PENALTY;
968     ED_minlen(f) = MAX(ED_minlen(f), ED_minlen(orig));
969     ED_weight(f) += ED_weight(orig);
970 }
971 
compile_edges(graph_t * ug,graph_t * Xg)972 static void compile_edges(graph_t * ug, graph_t * Xg)
973 {
974     node_t *n;
975     edge_t *e;
976     node_t *Xt, *Xh;
977     graph_t *tc, *hc;
978 
979     /* build edge constraints */
980     for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
981 	Xt = ND_rep(n);
982 	for (e = agfstout(ug, n); e; e = agnxtout(ug, e)) {
983 	    if (is_nonconstraint(e))
984 		continue;
985 	    Xh = ND_rep(find(aghead(e)));
986 	    if (Xt == Xh)
987 		continue;
988 
989 	    tc = ND_clust(agtail(e));
990 	    hc = ND_clust(aghead(e));
991 
992 	    if (is_internal_to_cluster(e)) {
993 		/* determine if graph requires reversed edge */
994 		if ((find(agtail(e)) == GD_maxrep(ND_clust(agtail(e))))
995 		    || (find(aghead(e)) == GD_minrep(ND_clust(aghead(e))))) {
996 		    node_t *temp = Xt;
997 		    Xt = Xh;
998 		    Xh = temp;
999 		}
1000 		strong(Xg, Xt, Xh, e);
1001 	    } else {
1002 		if (is_a_strong_cluster(tc) || is_a_strong_cluster(hc))
1003 		    weak(Xg, Xt, Xh, e);
1004 		else
1005 		    strong(Xg, Xt, Xh, e);
1006 	    }
1007 	}
1008     }
1009 }
1010 
compile_clusters(graph_t * g,graph_t * Xg,node_t * top,node_t * bot)1011 static void compile_clusters(graph_t* g, graph_t* Xg, node_t* top, node_t* bot)
1012 {
1013     node_t *n;
1014     node_t *rep;
1015     edge_t *e;
1016     graph_t *sub;
1017 
1018     if (is_a_cluster(g) && is_a_strong_cluster(g)) {
1019 	for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1020 	    if (agfstin(g, n) == 0) {
1021 		rep = ND_rep(find(n));
1022 		if (!top) top = makeXnode(Xg,TOPNODE);
1023 		agedge(Xg, top, rep, 0, 1);
1024 	    }
1025 	    if (agfstout(g, n) == 0) {
1026 		rep = ND_rep(find(n));
1027 		if (!bot)  bot = makeXnode(Xg,BOTNODE);
1028 		agedge(Xg, rep, bot, 0, 1);
1029 	    }
1030 	}
1031 	if (top && bot) {
1032 	    e = agedge(Xg, top, bot, 0, 1);
1033 	    merge(e, 0, STRONG_CLUSTER_WEIGHT);
1034 	}
1035     }
1036     for (sub = agfstsubg(g); sub; sub = agnxtsubg(sub))
1037 	compile_clusters(sub, Xg, top, bot);
1038 }
1039 
reverse_edge2(graph_t * g,edge_t * e)1040 static void reverse_edge2(graph_t * g, edge_t * e)
1041 {
1042     edge_t *rev;
1043 
1044     rev = agfindedge(g, aghead(e), agtail(e));
1045     if (!rev)
1046 	rev = agedge(g, aghead(e), agtail(e), 0, 1);
1047     merge(rev, ED_minlen(e), ED_weight(e));
1048     agdelete(g, e);
1049 }
1050 
dfs(graph_t * g,node_t * v)1051 static void dfs(graph_t * g, node_t * v)
1052 {
1053     edge_t *e, *f;
1054     node_t *w;
1055 
1056     if (ND_mark(v))
1057 	return;
1058     ND_mark(v) = TRUE;
1059     ND_onstack(v) = TRUE;
1060     for (e = agfstout(g, v); e; e = f) {
1061 	f = agnxtout(g, e);
1062 	w = aghead(e);
1063 	if (ND_onstack(w))
1064 	    reverse_edge2(g, e);
1065 	else {
1066 	    if (ND_mark(w) == FALSE)
1067 		dfs(g, w);
1068 	}
1069     }
1070     ND_onstack(v) = FALSE;
1071 }
1072 
break_cycles(graph_t * g)1073 static void break_cycles(graph_t * g)
1074 {
1075     node_t *n;
1076 
1077     for (n = agfstnode(g); n; n = agnxtnode(g, n))
1078 	ND_mark(n) = ND_onstack(n) = FALSE;
1079     for (n = agfstnode(g); n; n = agnxtnode(g, n))
1080 	dfs(g, n);
1081 }
1082 /* setMinMax:
1083  * This will only be called with the root graph or a cluster
1084  * which are guaranteed to contain nodes. Thus, leader will be
1085  * set.
1086  */
setMinMax(graph_t * g,int doRoot)1087 static void setMinMax (graph_t* g, int doRoot)
1088 {
1089     int c, v;
1090     node_t *n;
1091     node_t* leader = NULL;
1092 
1093       /* Do child clusters */
1094     for (c = 1; c <= GD_n_cluster(g); c++)
1095 	    setMinMax(GD_clust(g)[c], 0);
1096 
1097     if (!GD_parent(g) && !doRoot) // root graph
1098 	return;
1099 
1100     GD_minrank(g) = MAXSHORT;
1101     GD_maxrank(g) = -1;
1102     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1103 	v = ND_rank(n);
1104 	if (GD_maxrank(g) < v)
1105 	    GD_maxrank(g) = v;
1106 	if (GD_minrank(g) > v) {
1107 	    GD_minrank(g) = v;
1108 	    leader = n;
1109 	}
1110     }
1111     GD_leader(g) = leader;
1112 }
1113 
1114 /* readout_levels:
1115  * Store node rank information in original graph.
1116  * Set rank bounds in graph and clusters
1117  * Free added data structures.
1118  *
1119  * rank2 is called with balance=1, which ensures that minrank=0
1120  */
readout_levels(graph_t * g,graph_t * Xg,int ncc)1121 static void readout_levels(graph_t * g, graph_t * Xg, int ncc)
1122 {
1123     node_t *n;
1124     node_t *xn;
1125     int* minrk = NULL;
1126     int doRoot = 0;
1127 
1128     GD_minrank(g) = MAXSHORT;
1129     GD_maxrank(g) = -1;
1130     if (ncc > 1) {
1131 	int i;
1132 	minrk = N_NEW(ncc+1,int);
1133 	for (i = 1; i <= ncc; i++)
1134 	    minrk[i] = MAXSHORT;
1135     }
1136     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1137 	xn = ND_rep(find(n));
1138 	ND_rank(n) = ND_rank(xn);
1139 	if (GD_maxrank(g) < ND_rank(n))
1140 	    GD_maxrank(g) = ND_rank(n);
1141 	if (GD_minrank(g) > ND_rank(n))
1142 	    GD_minrank(g) = ND_rank(n);
1143 	if (minrk) {
1144 	    ND_comp(n) = ND_comp(xn);
1145 	    minrk[ND_comp(n)] = MIN(minrk[ND_comp(n)],ND_rank(n));
1146 	}
1147     }
1148     if (minrk) {
1149 	for (n = agfstnode(g); n; n = agnxtnode(g, n))
1150 	    ND_rank(n) -= minrk[ND_comp(n)];
1151 	/* Non-uniform shifting, so recompute maxrank/minrank of root graph */
1152 	doRoot = 1;
1153     }
1154     else if (GD_minrank(g) > 0) {  /* should never happen */
1155 	int delta = GD_minrank(g);
1156 	for (n = agfstnode(g); n; n = agnxtnode(g, n))
1157 	    ND_rank(n) -= delta;
1158 	GD_minrank(g) -= delta;
1159 	GD_maxrank(g) -= delta;
1160     }
1161 
1162     setMinMax(g, doRoot);
1163 
1164     /* release fastgraph memory from Xg */
1165     for (n = agfstnode(Xg); n; n = agnxtnode(Xg, n)) {
1166 	free_list(ND_in(n));
1167 	free_list(ND_out(n));
1168     }
1169 
1170     free(ND_alg(agfstnode(g)));
1171     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1172 	ND_alg(n) = NULL;
1173     }
1174     if (minrk)
1175 	free (minrk);
1176 }
1177 
dfscc(graph_t * g,node_t * n,int cc)1178 static void dfscc(graph_t * g, node_t * n, int cc)
1179 {
1180     edge_t *e;
1181     if (ND_comp(n) == 0) {
1182 	ND_comp(n) = cc;
1183 	for (e = agfstout(g, n); e; e = agnxtout(g, e))
1184 	    dfscc(g, aghead(e), cc);
1185 	for (e = agfstin(g, n); e; e = agnxtin(g, e))
1186 	    dfscc(g, agtail(e), cc);
1187     }
1188 }
1189 
connect_components(graph_t * g)1190 static int connect_components(graph_t * g)
1191 {
1192     int cc = 0;
1193     node_t *n;
1194 
1195     for (n = agfstnode(g); n; n = agnxtnode(g, n))
1196 	ND_comp(n) = 0;
1197     for (n = agfstnode(g); n; n = agnxtnode(g, n))
1198 	if (ND_comp(n) == 0)
1199 	    dfscc(g, n, ++cc);
1200     if (cc > 1) {
1201 	node_t *root = makeXnode(g, ROOT);
1202 	int ncc = 1;
1203 	for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1204 	    if (ND_comp(n) == ncc) {
1205 		(void) agedge(g, root, n, 0, 1);
1206 		ncc++;
1207 	    }
1208 	}
1209     }
1210     return (cc);
1211 }
1212 
add_fast_edges(graph_t * g)1213 static void add_fast_edges (graph_t * g)
1214 {
1215     node_t *n;
1216     edge_t *e;
1217     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1218 	for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
1219 	    elist_append(e, ND_out(n));
1220 	    elist_append(e, ND_in(aghead(e)));
1221 	}
1222     }
1223 }
1224 
my_init_graph(Agraph_t * g,Agobj_t * graph,void * arg)1225 static void my_init_graph(Agraph_t *g, Agobj_t *graph, void *arg)
1226 { int *sz = arg; agbindrec(graph,"level graph rec",sz[0],TRUE); }
my_init_node(Agraph_t * g,Agobj_t * node,void * arg)1227 static void my_init_node(Agraph_t *g, Agobj_t *node, void *arg)
1228 { int *sz = arg; agbindrec(node,"level node rec",sz[1],TRUE); }
my_init_edge(Agraph_t * g,Agobj_t * edge,void * arg)1229 static void my_init_edge(Agraph_t *g, Agobj_t *edge, void *arg)
1230 { int *sz = arg; agbindrec(edge,"level edge rec",sz[2],TRUE); }
1231 static Agcbdisc_t mydisc = { {my_init_graph,0,0}, {my_init_node,0,0}, {my_init_edge,0,0} };
1232 
1233 int infosizes[] = {
1234     sizeof(Agraphinfo_t),
1235     sizeof(Agnodeinfo_t),
1236     sizeof(Agedgeinfo_t)
1237 };
1238 
dot2_rank(graph_t * g,aspect_t * asp)1239 void dot2_rank(graph_t * g, aspect_t* asp)
1240 {
1241     int ssize;
1242     int ncc, maxiter = INT_MAX;
1243     char *s;
1244     graph_t *Xg;
1245 
1246     Last_node = NULL;
1247     Xg = agopen("level assignment constraints", Agstrictdirected, 0);
1248     agbindrec(Xg,"level graph rec",sizeof(Agraphinfo_t),TRUE);
1249     agpushdisc(Xg,&mydisc,infosizes);
1250 
1251     edgelabel_ranks(g);
1252 
1253     if ((s = agget(g, "nslimit1")))
1254 	maxiter = atof(s) * agnnodes(g);
1255     else
1256 	maxiter = INT_MAX;
1257 
1258     compile_samerank(g, 0);
1259     compile_nodes(g, Xg);
1260     compile_edges(g, Xg);
1261     compile_clusters(g, Xg, 0, 0);
1262     break_cycles(Xg);
1263     ncc = connect_components(Xg);
1264     add_fast_edges (Xg);
1265 
1266     if (asp) {
1267 	init_UF_size(Xg);
1268 	initEdgeTypes(Xg);
1269     }
1270 
1271     if ((s = agget(g, "searchsize")))
1272 	ssize = atoi(s);
1273     else
1274 	ssize = -1;
1275     rank2(Xg, 1, maxiter, ssize);
1276 /* fastgr(Xg); */
1277     readout_levels(g, Xg, ncc);
1278 #ifdef DEBUG
1279     fprintf (stderr, "Xg %d nodes %d edges\n", agnnodes(Xg), agnedges(Xg));
1280 #endif
1281     agclose(Xg);
1282 }
1283