Graphviz 14.1.3~dev.20260124.0732
Loading...
Searching...
No Matches
blocktree.c
Go to the documentation of this file.
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
11#include "config.h"
12
13#include <circogen/blocktree.h>
14#include <stdbool.h>
15#include <util/agxbuf.h>
16#include <util/debug.h>
17#include <util/gv_math.h>
18#include <util/list.h>
19
20static void addNode(block_t * bp, Agnode_t * n)
21{
22 agsubnode(bp->sub_graph, n,1);
23 BLOCK(n) = bp;
24}
25
27{
28 agxbuf name = {0};
29
30 agxbprint(&name, "_block_%d", state->blockCount++);
31 Agraph_t *subg = agsubg(g, agxbuse(&name), 1);
32 agxbfree(&name);
33 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); //node custom data
34 return subg;
35}
36
37static block_t *makeBlock(Agraph_t * g, circ_state * state)
38{
39 Agraph_t *subg = makeBlockGraph(g, state);
40 block_t *bp = mkBlock(subg);
41
42 return bp;
43}
44
45typedef LIST(Agedge_t *) estack_t;
46
47/* Current scheme adds articulation point to first non-trivial child
48 * block. If none exists, it will be added to its parent's block, if
49 * non-trivial, or else given its own block.
50 *
51 * FIX:
52 * This should be modified to:
53 * - allow user to specify which block gets a node, perhaps on per-node basis.
54 * - if an articulation point is not used in one of its non-trivial blocks,
55 * dummy edges should be added to preserve biconnectivity
56 * - turn on user-supplied blocks.
57 * - Post-process to move articulation point to largest block
58 */
59static void dfs(Agraph_t *g, Agnode_t *u, circ_state *state, bool isRoot,
60 estack_t *stk) {
61 LOWVAL(u) = VAL(u) = state->orderCount++;
62 for (Agedge_t *e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) {
63 Agnode_t *v = aghead (e);
64 if (v == u) {
65 v = agtail(e);
66 if (!EDGEORDER(e)) EDGEORDER(e) = -1;
67 }
68 else {
69 if (!EDGEORDER(e)) EDGEORDER(e) = 1;
70 }
71
72 if (VAL(v) == 0) { /* Since VAL(root) == 0, it gets treated as artificial cut point */
73 PARENT(v) = u;
74 LIST_PUSH_BACK(stk, e);
75 dfs(g, v, state, false, stk);
76 LOWVAL(u) = imin(LOWVAL(u), LOWVAL(v));
77 if (LOWVAL(v) >= VAL(u)) { /* u is an articulation point */
79 Agnode_t *np;
80 Agedge_t *ep;
81 do {
82 ep = LIST_POP_BACK(stk);
83 if (EDGEORDER(ep) == 1)
84 np = aghead (ep);
85 else
86 np = agtail (ep);
87 if (!BLOCK(np)) {
88 if (!block)
89 block = makeBlock(g, state);
90 addNode(block, np);
91 }
92 } while (ep != e);
93 if (block) { /* If block != NULL, it's not empty */
94 if (!BLOCK(u) && blockSize (block) > 1)
95 addNode(block, u);
96 if (isRoot && BLOCK(u) == block)
97 insertBlock(&state->bl, block);
98 else
99 appendBlock(&state->bl, block);
100 }
101 }
102 } else if (PARENT(u) != v) {
103 LOWVAL(u) = imin(LOWVAL(u), VAL(v));
104 }
105 }
106 if (isRoot && !BLOCK(u)) {
107 block_t *block = makeBlock(g, state);
108 addNode(block, u);
109 insertBlock(&state->bl, block);
110 }
111}
112
113static void find_blocks(Agraph_t * g, circ_state * state)
114{
115 Agnode_t *root = NULL;
116
117 /* check to see if there is a node which is set to be the root
118 */
119 if (state->rootname) {
120 root = agfindnode(g, state->rootname);
121 }
122 if (!root && state->N_root) {
123 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
124 if (late_bool(ORIGN(n), state->N_root, false)) {
125 root = n;
126 break;
127 }
128 }
129 }
130
131 if (!root)
132 root = agfstnode(g);
133 GV_DEBUG("root = %s", agnameof(root));
134 estack_t stk = {0};
135 dfs(g, root, state, true, &stk);
136 LIST_FREE(&stk);
137}
138
139/* Construct block tree by peeling nodes from block list in state.
140 * When done, return root. The block list is empty
141 * FIX: use largest block as root
142 */
144{
145 block_t *next;
146
147 find_blocks(g, state);
148
149 block_t *bp = state->bl.first; // if root chosen, will be first
150 /* Otherwise, just pick first as root */
151 block_t *root = bp;
152
153 /* Find node with minimum VAL value to find parent block */
154 /* FIX: Should be some way to avoid search below. */
155 for (bp = bp->next; bp; bp = next) {
156 Agnode_t *n;
158 Agnode_t *child;
159 Agraph_t *subg = bp->sub_graph;
160
161 child = n = agfstnode(subg);
162
163 int min = VAL(n);
164 parent = PARENT(n);
165 for (n = agnxtnode(subg, n); n; n = agnxtnode(subg, n)) {
166 if (VAL(n) < min) {
167 child = n;
168 min = VAL(n);
169 parent = PARENT(n);
170 }
171 }
173 CHILD(bp) = child;
174 next = bp->next; /* save next since list insertion destroys it */
175 appendBlock(&BLOCK(parent)->children, bp);
176 }
177 initBlocklist(&state->bl); /* zero out list */
178 return root;
179}
180
182{
183 for (block_t *child = bp->children.first, *next; child; child = next) {
184 next = child->next;
185 freeBlocktree(child);
186 }
187
188 freeBlock(bp);
189}
190
191#ifdef DEBUG
192static void indent(int i)
193{
194 while (i--)
195 fputs(" ", stderr);
196}
197
198void print_blocktree(block_t * sn, int depth)
199{
200 indent(depth);
201 Agraph_t *g = sn->sub_graph;
202 fprintf(stderr, "%s:", agnameof(g));
203 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
204 fprintf(stderr, " %s", agnameof(n));
205 }
206 fputs("\n", stderr);
207
208 depth++;
209 for (block_t *child = sn->children.first; child; child = child->next) {
210 print_blocktree(child, depth);
211 }
212}
213
214#endif
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static void dfs(Agraph_t *g, Agnode_t *u, bcstate *stp, Agnode_t *parent)
Definition bcomps.c:153
static Agraph_t * mkBlock(Agraph_t *g, bcstate *stp)
Definition bcomps.c:138
void insertBlock(blocklist_t *bl, block_t *bp)
add block at beginning
Definition block.c:60
int blockSize(block_t *sp)
Definition block.c:41
void freeBlock(block_t *sp)
Definition block.c:33
void initBlocklist(blocklist_t *bl)
Definition block.c:19
void appendBlock(blocklist_t *bl, block_t *bp)
add block at end
Definition block.c:47
static void find_blocks(Agraph_t *g, circ_state *state)
Definition blocktree.c:113
static block_t * makeBlock(Agraph_t *g, circ_state *state)
Definition blocktree.c:37
block_t * createBlocktree(Agraph_t *g, circ_state *state)
Definition blocktree.c:143
static Agraph_t * makeBlockGraph(Agraph_t *g, circ_state *state)
Definition blocktree.c:26
void freeBlocktree(block_t *bp)
Definition blocktree.c:181
static void addNode(block_t *bp, Agnode_t *n)
Definition blocktree.c:20
#define CHILD(b)
Definition block.h:50
#define BLOCK(n)
Definition circular.h:85
#define LOWVAL(n)
Definition circular.h:88
#define EDGEORDER(e)
Definition circular.h:78
#define SET_PARENT(n)
Definition circular.h:112
#define ORIGN(n)
Definition circular.h:82
#define PARENT(n)
Definition circular.h:84
#define VAL(n)
Definition circular.h:87
#define parent(i)
Definition closest.c:75
bool late_bool(void *obj, attrsym_t *attr, bool defaultValue)
Definition utils.c:97
helpers for verbose/debug printing
#define GV_DEBUG(...)
Definition debug.h:39
node NULL
Definition grammar.y:181
#define agtail(e)
Definition cgraph.h:977
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:98
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:89
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:254
#define agfindnode(g, n)
Definition types.h:611
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:55
static void indent(int ix)
Definition gv2gml.c:96
Arithmetic helper functions.
static int imin(int a, int b)
minimum of two integers
Definition gv_math.h:32
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_FREE(list)
Definition list.h:370
#define LIST_POP_BACK(list)
Definition list.h:407
#define LIST_PUSH_BACK(list, item)
Definition list.h:384
graph or subgraph
Definition cgraph.h:424
Definition block.h:26
blocklist_t children
Definition block.h:33
Agraph_t * sub_graph
Definition block.h:29
block_t * next
Definition block.h:28
block_t * first
Definition block.h:22
int orderCount
Definition circular.h:18
char * rootname
Definition circular.h:23
int blockCount
Definition circular.h:19
attrsym_t * N_root
Definition circular.h:22
blocklist_t bl
Definition circular.h:17