123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include <cgraph/gv_math.h>
- #include <circogen/blocktree.h>
- #include <stdbool.h>
- #include <util/agxbuf.h>
- static void addNode(block_t * bp, Agnode_t * n)
- {
- agsubnode(bp->sub_graph, n,1);
- BLOCK(n) = bp;
- }
- static Agraph_t *makeBlockGraph(Agraph_t * g, circ_state * state)
- {
- agxbuf name = {0};
- agxbprint(&name, "_block_%d", state->blockCount++);
- Agraph_t *subg = agsubg(g, agxbuse(&name), 1);
- agxbfree(&name);
- agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); //node custom data
- return subg;
- }
- static block_t *makeBlock(Agraph_t * g, circ_state * state)
- {
- Agraph_t *subg = makeBlockGraph(g, state);
- block_t *bp = mkBlock(subg);
- return bp;
- }
- DEFINE_LIST(estack, Agedge_t*)
- /* Current scheme adds articulation point to first non-trivial child
- * block. If none exists, it will be added to its parent's block, if
- * non-trivial, or else given its own block.
- *
- * FIX:
- * This should be modified to:
- * - allow user to specify which block gets a node, perhaps on per-node basis.
- * - if an articulation point is not used in one of its non-trivial blocks,
- * dummy edges should be added to preserve biconnectivity
- * - turn on user-supplied blocks.
- * - Post-process to move articulation point to largest block
- */
- static void dfs(Agraph_t *g, Agnode_t *u, circ_state *state, bool isRoot,
- estack_t *stk) {
- LOWVAL(u) = VAL(u) = state->orderCount++;
- for (Agedge_t *e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) {
- Agnode_t *v = aghead (e);
- if (v == u) {
- v = agtail(e);
- if (!EDGEORDER(e)) EDGEORDER(e) = -1;
- }
- else {
- if (!EDGEORDER(e)) EDGEORDER(e) = 1;
- }
- if (VAL(v) == 0) { /* Since VAL(root) == 0, it gets treated as artificial cut point */
- PARENT(v) = u;
- estack_push_back(stk, e);
- dfs(g, v, state, false, stk);
- LOWVAL(u) = imin(LOWVAL(u), LOWVAL(v));
- if (LOWVAL(v) >= VAL(u)) { /* u is an articulation point */
- block_t *block = NULL;
- Agnode_t *np;
- Agedge_t *ep;
- do {
- ep = estack_pop_back(stk);
- if (EDGEORDER(ep) == 1)
- np = aghead (ep);
- else
- np = agtail (ep);
- if (!BLOCK(np)) {
- if (!block)
- block = makeBlock(g, state);
- addNode(block, np);
- }
- } while (ep != e);
- if (block) { /* If block != NULL, it's not empty */
- if (!BLOCK(u) && blockSize (block) > 1)
- addNode(block, u);
- if (isRoot && BLOCK(u) == block)
- insertBlock(&state->bl, block);
- else
- appendBlock(&state->bl, block);
- }
- }
- } else if (PARENT(u) != v) {
- LOWVAL(u) = imin(LOWVAL(u), VAL(v));
- }
- }
- if (isRoot && !BLOCK(u)) {
- block_t *block = makeBlock(g, state);
- addNode(block, u);
- insertBlock(&state->bl, block);
- }
- }
- static void find_blocks(Agraph_t * g, circ_state * state)
- {
- Agnode_t *root = NULL;
- /* check to see if there is a node which is set to be the root
- */
- if (state->rootname) {
- root = agfindnode(g, state->rootname);
- }
- if (!root && state->N_root) {
- for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (late_bool(ORIGN(n), state->N_root, false)) {
- root = n;
- break;
- }
- }
- }
- if (!root)
- root = agfstnode(g);
- if (Verbose)
- fprintf (stderr, "root = %s\n", agnameof(root));
- estack_t stk = {0};
- dfs(g, root, state, true, &stk);
- estack_free(&stk);
- }
- /* Construct block tree by peeling nodes from block list in state.
- * When done, return root. The block list is empty
- * FIX: use largest block as root
- */
- block_t *createBlocktree(Agraph_t * g, circ_state * state)
- {
- block_t *next;
- find_blocks(g, state);
- block_t *bp = state->bl.first; // if root chosen, will be first
- /* Otherwise, just pick first as root */
- block_t *root = bp;
- /* Find node with minimum VAL value to find parent block */
- /* FIX: Should be some way to avoid search below. */
- for (bp = bp->next; bp; bp = next) {
- Agnode_t *n;
- Agnode_t *parent;
- Agnode_t *child;
- Agraph_t *subg = bp->sub_graph;
- child = n = agfstnode(subg);
- int min = VAL(n);
- parent = PARENT(n);
- for (n = agnxtnode(subg, n); n; n = agnxtnode(subg, n)) {
- if (VAL(n) < min) {
- child = n;
- min = VAL(n);
- parent = PARENT(n);
- }
- }
- SET_PARENT(parent);
- CHILD(bp) = child;
- next = bp->next; /* save next since list insertion destroys it */
- appendBlock(&BLOCK(parent)->children, bp);
- }
- initBlocklist(&state->bl); /* zero out list */
- return root;
- }
- void freeBlocktree(block_t * bp)
- {
- for (block_t *child = bp->children.first, *next; child; child = next) {
- next = child->next;
- freeBlocktree(child);
- }
- freeBlock(bp);
- }
- #ifdef DEBUG
- static void indent(int i)
- {
- while (i--)
- fputs(" ", stderr);
- }
- void print_blocktree(block_t * sn, int depth)
- {
- indent(depth);
- Agraph_t *g = sn->sub_graph;
- fprintf(stderr, "%s:", agnameof(g));
- for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
- fprintf(stderr, " %s", agnameof(n));
- }
- fputs("\n", stderr);
- depth++;
- for (block_t *child = sn->children.first; child; child = child->next) {
- print_blocktree(child, depth);
- }
- }
- #endif
|