| 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 | #include "blocktree.h" | 
|---|
| 16 |  | 
|---|
| 17 | static void addNode(block_t * bp, Agnode_t * n) | 
|---|
| 18 | { | 
|---|
| 19 | agsubnode(bp->sub_graph, n,1); | 
|---|
| 20 | BLOCK(n) = bp; | 
|---|
| 21 | } | 
|---|
| 22 |  | 
|---|
| 23 | static Agraph_t *makeBlockGraph(Agraph_t * g, circ_state * state) | 
|---|
| 24 | { | 
|---|
| 25 | char name[SMALLBUF]; | 
|---|
| 26 | Agraph_t *subg; | 
|---|
| 27 |  | 
|---|
| 28 | sprintf(name, "_block_%d", state->blockCount++); | 
|---|
| 29 | subg = agsubg(g, name,1); | 
|---|
| 30 | agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);	//node custom data | 
|---|
| 31 | return subg; | 
|---|
| 32 | } | 
|---|
| 33 |  | 
|---|
| 34 | static block_t *makeBlock(Agraph_t * g, circ_state * state) | 
|---|
| 35 | { | 
|---|
| 36 | Agraph_t *subg = makeBlockGraph(g, state); | 
|---|
| 37 | block_t *bp = mkBlock(subg); | 
|---|
| 38 |  | 
|---|
| 39 | return bp; | 
|---|
| 40 | } | 
|---|
| 41 |  | 
|---|
| 42 | typedef struct { | 
|---|
| 43 | Agedge_t *top; | 
|---|
| 44 | int sz; | 
|---|
| 45 | } estack; | 
|---|
| 46 |  | 
|---|
| 47 | static void | 
|---|
| 48 | push (estack* s, Agedge_t* e) | 
|---|
| 49 | { | 
|---|
| 50 | ENEXT(e) = s->top; | 
|---|
| 51 | s->top = e; | 
|---|
| 52 | s->sz += 1; | 
|---|
| 53 | } | 
|---|
| 54 |  | 
|---|
| 55 | static Agedge_t* | 
|---|
| 56 | pop (estack* s) | 
|---|
| 57 | { | 
|---|
| 58 | Agedge_t *top = s->top; | 
|---|
| 59 |  | 
|---|
| 60 | if (top) { | 
|---|
| 61 | assert(s->sz > 0); | 
|---|
| 62 | s->top = ENEXT(top); | 
|---|
| 63 | s->sz -= 1; | 
|---|
| 64 | } else { | 
|---|
| 65 | assert(0); | 
|---|
| 66 | } | 
|---|
| 67 |  | 
|---|
| 68 | return top; | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 |  | 
|---|
| 72 | /* dfs: | 
|---|
| 73 | * | 
|---|
| 74 | * Current scheme adds articulation point to first non-trivial child | 
|---|
| 75 | * block. If none exists, it will be added to its parent's block, if | 
|---|
| 76 | * non-trivial, or else given its own block. | 
|---|
| 77 | * | 
|---|
| 78 | * FIX: | 
|---|
| 79 | * This should be modified to: | 
|---|
| 80 | *  - allow user to specify which block gets a node, perhaps on per-node basis. | 
|---|
| 81 | *  - if an articulation point is not used in one of its non-trivial blocks, | 
|---|
| 82 | *    dummy edges should be added to preserve biconnectivity | 
|---|
| 83 | *  - turn on user-supplied blocks. | 
|---|
| 84 | *  - Post-process to move articulation point to largest block | 
|---|
| 85 | */ | 
|---|
| 86 | static void dfs(Agraph_t * g, Agnode_t * u, circ_state * state, int isRoot, estack* stk) | 
|---|
| 87 | { | 
|---|
| 88 | Agedge_t *e; | 
|---|
| 89 | Agnode_t *v; | 
|---|
| 90 |  | 
|---|
| 91 | LOWVAL(u) = VAL(u) = state->orderCount++; | 
|---|
| 92 | for (e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) { | 
|---|
| 93 | v = aghead (e); | 
|---|
| 94 | if (v == u) { | 
|---|
| 95 | v = agtail(e); | 
|---|
| 96 | if (!EDGEORDER(e)) EDGEORDER(e) = -1; | 
|---|
| 97 | } | 
|---|
| 98 | else { | 
|---|
| 99 | if (!EDGEORDER(e)) EDGEORDER(e) = 1; | 
|---|
| 100 | } | 
|---|
| 101 |  | 
|---|
| 102 | if (VAL(v) == 0) {   /* Since VAL(root) == 0, it gets treated as artificial cut point */ | 
|---|
| 103 | PARENT(v) = u; | 
|---|
| 104 | push(stk, e); | 
|---|
| 105 | dfs(g, v, state, 0, stk); | 
|---|
| 106 | LOWVAL(u) = MIN(LOWVAL(u), LOWVAL(v)); | 
|---|
| 107 | if (LOWVAL(v) >= VAL(u)) {       /* u is an articulation point */ | 
|---|
| 108 | block_t *block = NULL; | 
|---|
| 109 | Agnode_t *np; | 
|---|
| 110 | Agedge_t *ep; | 
|---|
| 111 | do { | 
|---|
| 112 | ep = pop(stk); | 
|---|
| 113 | if (EDGEORDER(ep) == 1) | 
|---|
| 114 | np = aghead (ep); | 
|---|
| 115 | else | 
|---|
| 116 | np = agtail (ep); | 
|---|
| 117 | if (!BLOCK(np)) { | 
|---|
| 118 | if (!block) | 
|---|
| 119 | block = makeBlock(g, state); | 
|---|
| 120 | addNode(block, np); | 
|---|
| 121 | } | 
|---|
| 122 | } while (ep != e); | 
|---|
| 123 | if (block) {	/* If block != NULL, it's not empty */ | 
|---|
| 124 | if (!BLOCK(u) && blockSize (block) > 1) | 
|---|
| 125 | addNode(block, u); | 
|---|
| 126 | if (isRoot && (BLOCK(u) == block)) | 
|---|
| 127 | insertBlock(&state->bl, block); | 
|---|
| 128 | else | 
|---|
| 129 | appendBlock(&state->bl, block); | 
|---|
| 130 | } | 
|---|
| 131 | } | 
|---|
| 132 | } else if (PARENT(u) != v) { | 
|---|
| 133 | LOWVAL(u) = MIN(LOWVAL(u), VAL(v)); | 
|---|
| 134 | } | 
|---|
| 135 | } | 
|---|
| 136 | if (isRoot && !BLOCK(u)) { | 
|---|
| 137 | block_t *block = makeBlock(g, state); | 
|---|
| 138 | addNode(block, u); | 
|---|
| 139 | insertBlock(&state->bl, block); | 
|---|
| 140 | } | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 |  | 
|---|
| 144 | /* find_blocks: | 
|---|
| 145 | */ | 
|---|
| 146 | static void find_blocks(Agraph_t * g, circ_state * state) | 
|---|
| 147 | { | 
|---|
| 148 | Agnode_t *n; | 
|---|
| 149 | Agnode_t *root = NULL; | 
|---|
| 150 | estack stk; | 
|---|
| 151 |  | 
|---|
| 152 | /*      check to see if there is a node which is set to be the root | 
|---|
| 153 | */ | 
|---|
| 154 | if (state->rootname) { | 
|---|
| 155 | root = agfindnode(g, state->rootname); | 
|---|
| 156 | } | 
|---|
| 157 | if (!root && state->N_root) { | 
|---|
| 158 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { | 
|---|
| 159 | if (late_bool(ORIGN(n), state->N_root, 0)) { | 
|---|
| 160 | root = n; | 
|---|
| 161 | break; | 
|---|
| 162 | } | 
|---|
| 163 | } | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | if (!root) | 
|---|
| 167 | root = agfstnode(g); | 
|---|
| 168 | if (Verbose) | 
|---|
| 169 | fprintf (stderr, "root = %s\n", agnameof(root)); | 
|---|
| 170 | stk.sz = 0; | 
|---|
| 171 | stk.top = NULL; | 
|---|
| 172 | dfs(g, root, state, 1, &stk); | 
|---|
| 173 |  | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | /* create_block_tree: | 
|---|
| 177 | * Construct block tree by peeling nodes from block list in state. | 
|---|
| 178 | * When done, return root. The block list is empty | 
|---|
| 179 | * FIX: use largest block as root | 
|---|
| 180 | */ | 
|---|
| 181 | block_t *createBlocktree(Agraph_t * g, circ_state * state) | 
|---|
| 182 | { | 
|---|
| 183 | block_t *bp; | 
|---|
| 184 | block_t *next; | 
|---|
| 185 | block_t *root; | 
|---|
| 186 | int min; | 
|---|
| 187 | /* int        ordercnt; */ | 
|---|
| 188 |  | 
|---|
| 189 | find_blocks(g, state); | 
|---|
| 190 |  | 
|---|
| 191 | bp = state->bl.first;	/* if root chosen, will be first */ | 
|---|
| 192 | /* Otherwise, just pick first as root */ | 
|---|
| 193 | root = bp; | 
|---|
| 194 |  | 
|---|
| 195 | /* Find node with minimum VAL value to find parent block */ | 
|---|
| 196 | /* FIX: Should be some way to avoid search below.               */ | 
|---|
| 197 | /* ordercnt = state->orderCount;  */ | 
|---|
| 198 | for (bp = bp->next; bp; bp = next) { | 
|---|
| 199 | Agnode_t *n; | 
|---|
| 200 | Agnode_t *parent; | 
|---|
| 201 | Agnode_t *child; | 
|---|
| 202 | Agraph_t *subg = bp->sub_graph; | 
|---|
| 203 |  | 
|---|
| 204 | child = n = agfstnode(subg); | 
|---|
| 205 |  | 
|---|
| 206 | min = VAL(n); | 
|---|
| 207 | parent = PARENT(n); | 
|---|
| 208 | for (n = agnxtnode(subg, n); n; n = agnxtnode(subg, n)) { | 
|---|
| 209 | if (VAL(n) < min) { | 
|---|
| 210 | child = n; | 
|---|
| 211 | min = VAL(n); | 
|---|
| 212 | parent = PARENT(n); | 
|---|
| 213 | } | 
|---|
| 214 | } | 
|---|
| 215 | SET_PARENT(parent); | 
|---|
| 216 | CHILD(bp) = child; | 
|---|
| 217 | next = bp->next;	/* save next since list insertion destroys it */ | 
|---|
| 218 | appendBlock(&(BLOCK(parent)->children), bp); | 
|---|
| 219 | } | 
|---|
| 220 | initBlocklist(&state->bl);	/* zero out list */ | 
|---|
| 221 | return root; | 
|---|
| 222 | } | 
|---|
| 223 |  | 
|---|
| 224 | void freeBlocktree(block_t * bp) | 
|---|
| 225 | { | 
|---|
| 226 | block_t *child; | 
|---|
| 227 | block_t *next; | 
|---|
| 228 |  | 
|---|
| 229 | for (child = bp->children.first; child; child = next) { | 
|---|
| 230 | next = child->next; | 
|---|
| 231 | freeBlocktree(child); | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 | freeBlock(bp); | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | #ifdef DEBUG | 
|---|
| 238 | static void indent(int i) | 
|---|
| 239 | { | 
|---|
| 240 | while (i--) | 
|---|
| 241 | fputs( "  ", stderr); | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 | void print_blocktree(block_t * sn, int depth) | 
|---|
| 245 | { | 
|---|
| 246 | block_t *child; | 
|---|
| 247 | Agnode_t *n; | 
|---|
| 248 | Agraph_t *g; | 
|---|
| 249 |  | 
|---|
| 250 | indent(depth); | 
|---|
| 251 | g = sn->sub_graph; | 
|---|
| 252 | fprintf(stderr, "%s:", agnameof(g)); | 
|---|
| 253 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { | 
|---|
| 254 | fprintf(stderr, " %s", agnameof(n)); | 
|---|
| 255 | } | 
|---|
| 256 | fputs( "\n", stderr); | 
|---|
| 257 |  | 
|---|
| 258 | depth++; | 
|---|
| 259 | for (child = sn->children.first; child; child = child->next) { | 
|---|
| 260 | print_blocktree(child, depth); | 
|---|
| 261 | } | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 | #endif | 
|---|
| 265 |  | 
|---|