blocktree.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include <cgraph/gv_math.h>
  11. #include <circogen/blocktree.h>
  12. #include <stdbool.h>
  13. #include <util/agxbuf.h>
  14. static void addNode(block_t * bp, Agnode_t * n)
  15. {
  16. agsubnode(bp->sub_graph, n,1);
  17. BLOCK(n) = bp;
  18. }
  19. static Agraph_t *makeBlockGraph(Agraph_t * g, circ_state * state)
  20. {
  21. agxbuf name = {0};
  22. agxbprint(&name, "_block_%d", state->blockCount++);
  23. Agraph_t *subg = agsubg(g, agxbuse(&name), 1);
  24. agxbfree(&name);
  25. agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); //node custom data
  26. return subg;
  27. }
  28. static block_t *makeBlock(Agraph_t * g, circ_state * state)
  29. {
  30. Agraph_t *subg = makeBlockGraph(g, state);
  31. block_t *bp = mkBlock(subg);
  32. return bp;
  33. }
  34. DEFINE_LIST(estack, Agedge_t*)
  35. /* Current scheme adds articulation point to first non-trivial child
  36. * block. If none exists, it will be added to its parent's block, if
  37. * non-trivial, or else given its own block.
  38. *
  39. * FIX:
  40. * This should be modified to:
  41. * - allow user to specify which block gets a node, perhaps on per-node basis.
  42. * - if an articulation point is not used in one of its non-trivial blocks,
  43. * dummy edges should be added to preserve biconnectivity
  44. * - turn on user-supplied blocks.
  45. * - Post-process to move articulation point to largest block
  46. */
  47. static void dfs(Agraph_t *g, Agnode_t *u, circ_state *state, bool isRoot,
  48. estack_t *stk) {
  49. LOWVAL(u) = VAL(u) = state->orderCount++;
  50. for (Agedge_t *e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) {
  51. Agnode_t *v = aghead (e);
  52. if (v == u) {
  53. v = agtail(e);
  54. if (!EDGEORDER(e)) EDGEORDER(e) = -1;
  55. }
  56. else {
  57. if (!EDGEORDER(e)) EDGEORDER(e) = 1;
  58. }
  59. if (VAL(v) == 0) { /* Since VAL(root) == 0, it gets treated as artificial cut point */
  60. PARENT(v) = u;
  61. estack_push_back(stk, e);
  62. dfs(g, v, state, false, stk);
  63. LOWVAL(u) = imin(LOWVAL(u), LOWVAL(v));
  64. if (LOWVAL(v) >= VAL(u)) { /* u is an articulation point */
  65. block_t *block = NULL;
  66. Agnode_t *np;
  67. Agedge_t *ep;
  68. do {
  69. ep = estack_pop_back(stk);
  70. if (EDGEORDER(ep) == 1)
  71. np = aghead (ep);
  72. else
  73. np = agtail (ep);
  74. if (!BLOCK(np)) {
  75. if (!block)
  76. block = makeBlock(g, state);
  77. addNode(block, np);
  78. }
  79. } while (ep != e);
  80. if (block) { /* If block != NULL, it's not empty */
  81. if (!BLOCK(u) && blockSize (block) > 1)
  82. addNode(block, u);
  83. if (isRoot && BLOCK(u) == block)
  84. insertBlock(&state->bl, block);
  85. else
  86. appendBlock(&state->bl, block);
  87. }
  88. }
  89. } else if (PARENT(u) != v) {
  90. LOWVAL(u) = imin(LOWVAL(u), VAL(v));
  91. }
  92. }
  93. if (isRoot && !BLOCK(u)) {
  94. block_t *block = makeBlock(g, state);
  95. addNode(block, u);
  96. insertBlock(&state->bl, block);
  97. }
  98. }
  99. static void find_blocks(Agraph_t * g, circ_state * state)
  100. {
  101. Agnode_t *root = NULL;
  102. /* check to see if there is a node which is set to be the root
  103. */
  104. if (state->rootname) {
  105. root = agfindnode(g, state->rootname);
  106. }
  107. if (!root && state->N_root) {
  108. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  109. if (late_bool(ORIGN(n), state->N_root, false)) {
  110. root = n;
  111. break;
  112. }
  113. }
  114. }
  115. if (!root)
  116. root = agfstnode(g);
  117. if (Verbose)
  118. fprintf (stderr, "root = %s\n", agnameof(root));
  119. estack_t stk = {0};
  120. dfs(g, root, state, true, &stk);
  121. estack_free(&stk);
  122. }
  123. /* Construct block tree by peeling nodes from block list in state.
  124. * When done, return root. The block list is empty
  125. * FIX: use largest block as root
  126. */
  127. block_t *createBlocktree(Agraph_t * g, circ_state * state)
  128. {
  129. block_t *next;
  130. find_blocks(g, state);
  131. block_t *bp = state->bl.first; // if root chosen, will be first
  132. /* Otherwise, just pick first as root */
  133. block_t *root = bp;
  134. /* Find node with minimum VAL value to find parent block */
  135. /* FIX: Should be some way to avoid search below. */
  136. for (bp = bp->next; bp; bp = next) {
  137. Agnode_t *n;
  138. Agnode_t *parent;
  139. Agnode_t *child;
  140. Agraph_t *subg = bp->sub_graph;
  141. child = n = agfstnode(subg);
  142. int min = VAL(n);
  143. parent = PARENT(n);
  144. for (n = agnxtnode(subg, n); n; n = agnxtnode(subg, n)) {
  145. if (VAL(n) < min) {
  146. child = n;
  147. min = VAL(n);
  148. parent = PARENT(n);
  149. }
  150. }
  151. SET_PARENT(parent);
  152. CHILD(bp) = child;
  153. next = bp->next; /* save next since list insertion destroys it */
  154. appendBlock(&BLOCK(parent)->children, bp);
  155. }
  156. initBlocklist(&state->bl); /* zero out list */
  157. return root;
  158. }
  159. void freeBlocktree(block_t * bp)
  160. {
  161. for (block_t *child = bp->children.first, *next; child; child = next) {
  162. next = child->next;
  163. freeBlocktree(child);
  164. }
  165. freeBlock(bp);
  166. }
  167. #ifdef DEBUG
  168. static void indent(int i)
  169. {
  170. while (i--)
  171. fputs(" ", stderr);
  172. }
  173. void print_blocktree(block_t * sn, int depth)
  174. {
  175. indent(depth);
  176. Agraph_t *g = sn->sub_graph;
  177. fprintf(stderr, "%s:", agnameof(g));
  178. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  179. fprintf(stderr, " %s", agnameof(n));
  180. }
  181. fputs("\n", stderr);
  182. depth++;
  183. for (block_t *child = sn->children.first; child; child = child->next) {
  184. print_blocktree(child, depth);
  185. }
  186. }
  187. #endif