ccomps.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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/cgraph.h>
  11. #include <cgraph/gv_ctype.h>
  12. #include <cgraph/list.h>
  13. #include <common/render.h>
  14. #include <common/utils.h>
  15. #include <limits.h>
  16. #include <pack/pack.h>
  17. #include <stdbool.h>
  18. #include <stdlib.h>
  19. #include <util/agxbuf.h>
  20. #include <util/alloc.h>
  21. #include <util/prisize_t.h>
  22. DEFINE_LIST(node_stack, Agnode_t *)
  23. typedef struct {
  24. node_stack_t data;
  25. void (*actionfn)(Agnode_t *, void *);
  26. bool (*markfn)(Agnode_t *, int);
  27. } stk_t;
  28. /// does `n` have a mark set?
  29. static bool marked(const stk_t *stk, Agnode_t *n) { return stk->markfn(n, -1); }
  30. /// set a mark on `n`
  31. static void mark(const stk_t *stk, Agnode_t *n) { stk->markfn(n, 1); }
  32. /// unset a mark on `n`
  33. static void unmark(const stk_t *stk, Agnode_t *n) { stk->markfn(n, 0); }
  34. static void initStk(stk_t *sp, void (*actionfn)(Agnode_t *, void *),
  35. bool (*markfn)(Agnode_t *, int)) {
  36. sp->data = (node_stack_t){0};
  37. sp->actionfn = actionfn;
  38. sp->markfn = markfn;
  39. }
  40. static void freeStk(stk_t *sp) { node_stack_free(&sp->data); }
  41. static void push(stk_t *sp, Agnode_t *np) {
  42. mark(sp, np);
  43. node_stack_push_back(&sp->data, np);
  44. }
  45. static Agnode_t *pop(stk_t *sp) {
  46. if (node_stack_is_empty(&sp->data)) {
  47. return NULL;
  48. }
  49. return node_stack_pop_back(&sp->data);
  50. }
  51. static size_t dfs(Agraph_t *g, Agnode_t *n, void *state, stk_t *stk) {
  52. Agedge_t *e;
  53. Agnode_t *other;
  54. size_t cnt = 0;
  55. push(stk, n);
  56. while ((n = pop(stk))) {
  57. cnt++;
  58. if (stk->actionfn)
  59. stk->actionfn(n, state);
  60. for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
  61. if ((other = agtail(e)) == n)
  62. other = aghead(e);
  63. if (!marked(stk, other))
  64. push(stk, other);
  65. }
  66. }
  67. return cnt;
  68. }
  69. static bool isLegal(const char *p) {
  70. char c;
  71. while ((c = *p++)) {
  72. if (c != '_' && !gv_isalnum(c))
  73. return false;
  74. }
  75. return true;
  76. }
  77. static void insertFn(Agnode_t *n, void *state) { agsubnode(state, n, 1); }
  78. static bool markFn(Agnode_t *n, int v) {
  79. if (v < 0)
  80. return ND_mark(n) != 0;
  81. const size_t ret = ND_mark(n);
  82. ND_mark(n) = v != 0;
  83. return ret != 0;
  84. }
  85. static void setPrefix(agxbuf *xb, const char *pfx) {
  86. if (!pfx || !isLegal(pfx)) {
  87. pfx = "_cc_";
  88. }
  89. agxbput(xb, pfx);
  90. }
  91. DEFINE_LIST(Agraphs, Agraph_t *)
  92. /* pccomps:
  93. * Return an array of subgraphs consisting of the connected
  94. * components of graph g. The number of components is returned in ncc.
  95. * All pinned nodes are in one component.
  96. * If pfx is non-null and a legal graph name, we use it as the prefix
  97. * for the name of the subgraphs created. If not, a simple default is used.
  98. * If pinned is non-null, *pinned set to 1 if pinned nodes found
  99. * and the first component is the one containing the pinned nodes.
  100. * Note that the component subgraphs do not contain any edges. These must
  101. * be obtained from the root graph.
  102. * Return NULL if graph is empty.
  103. */
  104. Agraph_t **pccomps(Agraph_t *g, size_t *ncc, char *pfx, bool *pinned) {
  105. agxbuf name = {0};
  106. Agraph_t *out = NULL;
  107. Agnode_t *n;
  108. bool pin = false;
  109. stk_t stk;
  110. if (agnnodes(g) == 0) {
  111. *ncc = 0;
  112. return NULL;
  113. }
  114. Agraphs_t ccs = {0};
  115. initStk(&stk, insertFn, markFn);
  116. for (n = agfstnode(g); n; n = agnxtnode(g, n))
  117. unmark(&stk, n);
  118. /* Component with pinned nodes */
  119. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  120. if (marked(&stk, n) || !isPinned(n))
  121. continue;
  122. if (!out) {
  123. setPrefix(&name, pfx);
  124. agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
  125. out = agsubg(g, agxbuse(&name), 1);
  126. agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t),
  127. true); // node custom data
  128. Agraphs_append(&ccs, out);
  129. pin = true;
  130. }
  131. dfs(g, n, out, &stk);
  132. }
  133. /* Remaining nodes */
  134. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  135. if (marked(&stk, n))
  136. continue;
  137. setPrefix(&name, pfx);
  138. agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
  139. out = agsubg(g, agxbuse(&name), 1);
  140. agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t),
  141. true); // node custom data
  142. dfs(g, n, out, &stk);
  143. Agraphs_append(&ccs, out);
  144. }
  145. freeStk(&stk);
  146. agxbfree(&name);
  147. *ncc = Agraphs_size(&ccs);
  148. *pinned = pin;
  149. return Agraphs_detach(&ccs);
  150. }
  151. /* ccomps:
  152. * Return an array of subgraphs consisting of the connected
  153. * components of graph g. The number of components is returned in ncc.
  154. * If pfx is non-null and a legal graph name, we use it as the prefix
  155. * for the name of the subgraphs created. If not, a simple default is used.
  156. * Note that the component subgraphs do not contain any edges. These must
  157. * be obtained from the root graph.
  158. * Returns NULL on error or if graph is empty.
  159. */
  160. Agraph_t **ccomps(Agraph_t *g, size_t *ncc, char *pfx) {
  161. agxbuf name = {0};
  162. Agraph_t *out;
  163. Agnode_t *n;
  164. stk_t stk;
  165. if (agnnodes(g) == 0) {
  166. *ncc = 0;
  167. return NULL;
  168. }
  169. Agraphs_t ccs = {0};
  170. initStk(&stk, insertFn, markFn);
  171. for (n = agfstnode(g); n; n = agnxtnode(g, n))
  172. unmark(&stk, n);
  173. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  174. if (marked(&stk, n))
  175. continue;
  176. setPrefix(&name, pfx);
  177. agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
  178. out = agsubg(g, agxbuse(&name), 1);
  179. agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t),
  180. true); // node custom data
  181. dfs(g, n, out, &stk);
  182. Agraphs_append(&ccs, out);
  183. }
  184. freeStk(&stk);
  185. agxbfree(&name);
  186. *ncc = Agraphs_size(&ccs);
  187. return Agraphs_detach(&ccs);
  188. }
  189. typedef struct {
  190. Agrec_t h;
  191. char cc_subg; /* true iff subgraph corresponds to a component */
  192. } ccgraphinfo_t;
  193. typedef struct {
  194. Agrec_t h;
  195. char mark;
  196. union {
  197. Agraph_t *g;
  198. Agnode_t *n;
  199. void *v;
  200. } ptr;
  201. } ccgnodeinfo_t;
  202. #define GRECNAME "ccgraphinfo"
  203. #define NRECNAME "ccgnodeinfo"
  204. #define GD_cc_subg(g) (((ccgraphinfo_t *)aggetrec(g, GRECNAME, 0))->cc_subg)
  205. #ifdef DEBUG
  206. Agnode_t *dnodeOf(Agnode_t *v) {
  207. ccgnodeinfo_t *ip = (ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0);
  208. if (ip)
  209. return ip->ptr.n;
  210. fprintf(stderr, "nodeinfo undefined\n");
  211. return NULL;
  212. }
  213. void dnodeSet(Agnode_t *v, Agnode_t *n) {
  214. ((ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0))->ptr.n = n;
  215. }
  216. #else
  217. #define dnodeOf(v) (((ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0))->ptr.n)
  218. #define dnodeSet(v, w) (((ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0))->ptr.n = w)
  219. #endif
  220. #define ptrOf(np) (((ccgnodeinfo_t *)((np)->base.data))->ptr.v)
  221. #define nodeOf(np) (((ccgnodeinfo_t *)((np)->base.data))->ptr.n)
  222. #define clustOf(np) (((ccgnodeinfo_t *)((np)->base.data))->ptr.g)
  223. #define clMark(n) (((ccgnodeinfo_t *)(n->base.data))->mark)
  224. /* deriveClusters:
  225. * Construct nodes in derived graph corresponding top-level clusters.
  226. * Since a cluster might be wrapped in a subgraph, we need to traverse
  227. * down into the tree of subgraphs
  228. */
  229. static void deriveClusters(Agraph_t *dg, Agraph_t *g) {
  230. Agraph_t *subg;
  231. Agnode_t *dn;
  232. Agnode_t *n;
  233. for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
  234. if (is_a_cluster(subg)) {
  235. dn = agnode(dg, agnameof(subg), 1);
  236. agbindrec(dn, NRECNAME, sizeof(ccgnodeinfo_t), true);
  237. clustOf(dn) = subg;
  238. for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
  239. if (dnodeOf(n)) {
  240. fprintf(stderr,
  241. "Error: node \"%s\" belongs to two non-nested clusters "
  242. "\"%s\" and \"%s\"\n",
  243. agnameof(n), agnameof(subg), agnameof(dnodeOf(n)));
  244. }
  245. dnodeSet(n, dn);
  246. }
  247. } else {
  248. deriveClusters(dg, subg);
  249. }
  250. }
  251. }
  252. /* deriveGraph:
  253. * Create derived graph dg of g where nodes correspond to top-level nodes
  254. * or clusters, and there is an edge in dg if there is an edge in g
  255. * between any nodes in the respective clusters.
  256. */
  257. static Agraph_t *deriveGraph(Agraph_t *g) {
  258. Agraph_t *dg;
  259. Agnode_t *dn;
  260. Agnode_t *n;
  261. dg = agopen("dg", Agstrictundirected, NULL);
  262. deriveClusters(dg, g);
  263. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  264. if (dnodeOf(n))
  265. continue;
  266. dn = agnode(dg, agnameof(n), 1);
  267. agbindrec(dn, NRECNAME, sizeof(ccgnodeinfo_t), true);
  268. nodeOf(dn) = n;
  269. dnodeSet(n, dn);
  270. }
  271. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  272. Agedge_t *e;
  273. Agnode_t *hd;
  274. Agnode_t *tl = dnodeOf(n);
  275. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  276. hd = aghead(e);
  277. hd = dnodeOf(hd);
  278. if (hd == tl)
  279. continue;
  280. if (hd > tl)
  281. agedge(dg, tl, hd, NULL, 1);
  282. else
  283. agedge(dg, hd, tl, NULL, 1);
  284. }
  285. }
  286. return dg;
  287. }
  288. /* unionNodes:
  289. * Add all nodes in cluster nodes of dg to g
  290. */
  291. static void unionNodes(Agraph_t *dg, Agraph_t *g) {
  292. Agnode_t *n;
  293. Agnode_t *dn;
  294. Agraph_t *clust;
  295. for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
  296. if (AGTYPE(ptrOf(dn)) == AGNODE) {
  297. agsubnode(g, nodeOf(dn), 1);
  298. } else {
  299. clust = clustOf(dn);
  300. for (n = agfstnode(clust); n; n = agnxtnode(clust, n))
  301. agsubnode(g, n, 1);
  302. }
  303. }
  304. }
  305. static bool clMarkFn(Agnode_t *n, int v) {
  306. int ret;
  307. if (v < 0)
  308. return clMark(n) != 0;
  309. ret = clMark(n);
  310. clMark(n) = (char)v;
  311. return ret != 0;
  312. }
  313. typedef struct {
  314. Agrec_t h;
  315. Agraph_t *orig;
  316. } orig_t;
  317. #define ORIG_REC "orig"
  318. Agraph_t *mapClust(Agraph_t *cl) {
  319. orig_t *op = (orig_t *)aggetrec(cl, ORIG_REC, 0);
  320. assert(op);
  321. return op->orig;
  322. }
  323. /* projectG:
  324. * If any nodes of subg are in g, create a subgraph of g
  325. * and fill it with all nodes of subg in g and their induced
  326. * edges in subg. Copy the attributes of subg to g. Return the subgraph.
  327. * If not, return null.
  328. * If subg is a cluster, the new subgraph will contain a pointer to it
  329. * in the record "orig".
  330. */
  331. static Agraph_t *projectG(Agraph_t *subg, Agraph_t *g, int inCluster) {
  332. Agraph_t *proj = NULL;
  333. Agnode_t *n;
  334. Agnode_t *m;
  335. orig_t *op;
  336. for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
  337. if ((m = agfindnode(g, agnameof(n)))) {
  338. if (proj == NULL) {
  339. proj = agsubg(g, agnameof(subg), 1);
  340. }
  341. agsubnode(proj, m, 1);
  342. }
  343. }
  344. if (!proj && inCluster) {
  345. proj = agsubg(g, agnameof(subg), 1);
  346. }
  347. if (proj) {
  348. (void)graphviz_node_induce(proj, subg);
  349. agcopyattr(subg, proj);
  350. if (is_a_cluster(proj)) {
  351. op = agbindrec(proj, ORIG_REC, sizeof(orig_t), false);
  352. op->orig = subg;
  353. }
  354. }
  355. return proj;
  356. }
  357. /* subgInduce:
  358. * Project subgraphs of root graph on subgraph.
  359. * If non-empty, add to subgraph.
  360. */
  361. static void subgInduce(Agraph_t *root, Agraph_t *g, int inCluster) {
  362. Agraph_t *subg;
  363. Agraph_t *proj;
  364. int in_cluster;
  365. for (subg = agfstsubg(root); subg; subg = agnxtsubg(subg)) {
  366. if (GD_cc_subg(subg))
  367. continue;
  368. if ((proj = projectG(subg, g, inCluster))) {
  369. in_cluster = (inCluster || is_a_cluster(subg));
  370. subgInduce(subg, proj, in_cluster);
  371. }
  372. }
  373. }
  374. static void subGInduce(Agraph_t *g, Agraph_t *out) { subgInduce(g, out, 0); }
  375. /* cccomps:
  376. * Decompose g into "connected" components, where nodes are connected
  377. * either by an edge or by being in the same cluster. The components
  378. * are returned in an array of subgraphs. ncc indicates how many components
  379. * there are. The subgraphs use the prefix pfx in their names, if non-NULL.
  380. * Note that cluster subgraph of the main graph, corresponding to a component,
  381. * is cloned within the subgraph. Each cloned cluster contains a record pointing
  382. * to the real cluster.
  383. */
  384. Agraph_t **cccomps(Agraph_t *g, size_t *ncc, char *pfx) {
  385. Agraph_t *dg;
  386. size_t n_cnt, e_cnt;
  387. agxbuf name = {0};
  388. Agraph_t *out;
  389. Agraph_t *dout;
  390. Agnode_t *dn;
  391. stk_t stk;
  392. int sz = (int)sizeof(ccgraphinfo_t);
  393. if (agnnodes(g) == 0) {
  394. *ncc = 0;
  395. return NULL;
  396. }
  397. /* Bind ccgraphinfo to graph and all subgraphs */
  398. aginit(g, AGRAPH, GRECNAME, -sz, false);
  399. /* Bind ccgraphinfo to graph and all subgraphs */
  400. aginit(g, AGNODE, NRECNAME, sizeof(ccgnodeinfo_t), false);
  401. dg = deriveGraph(g);
  402. size_t ccs_length = (size_t)agnnodes(dg);
  403. Agraphs_t ccs = {0};
  404. Agraphs_reserve(&ccs, ccs_length);
  405. initStk(&stk, insertFn, clMarkFn);
  406. for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
  407. if (marked(&stk, dn))
  408. continue;
  409. setPrefix(&name, pfx);
  410. agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
  411. char *name_str = agxbuse(&name);
  412. dout = agsubg(dg, name_str, 1);
  413. out = agsubg(g, name_str, 1);
  414. agbindrec(out, GRECNAME, sizeof(ccgraphinfo_t), false);
  415. GD_cc_subg(out) = 1;
  416. n_cnt = dfs(dg, dn, dout, &stk);
  417. unionNodes(dout, out);
  418. e_cnt = graphviz_node_induce(out, NULL);
  419. subGInduce(g, out);
  420. Agraphs_append(&ccs, out);
  421. agdelete(dg, dout);
  422. if (Verbose)
  423. fprintf(stderr,
  424. "(%4" PRISIZE_T ") %7" PRISIZE_T " nodes %7" PRISIZE_T " edges\n",
  425. Agraphs_size(&ccs) - 1, n_cnt, e_cnt);
  426. }
  427. if (Verbose)
  428. fprintf(stderr,
  429. " %7d nodes %7d edges %7" PRISIZE_T " components %s\n",
  430. agnnodes(g), agnedges(g), Agraphs_size(&ccs), agnameof(g));
  431. agclose(dg);
  432. agclean(g, AGRAPH, GRECNAME);
  433. agclean(g, AGNODE, NRECNAME);
  434. freeStk(&stk);
  435. agxbfree(&name);
  436. *ncc = Agraphs_size(&ccs);
  437. return Agraphs_detach(&ccs);
  438. }
  439. /* isConnected:
  440. * Returns 1 if the graph is connected.
  441. * Returns 0 if the graph is not connected.
  442. * Returns -1 if the graph is error.
  443. */
  444. int isConnected(Agraph_t *g) {
  445. Agnode_t *n;
  446. int ret = 1;
  447. size_t cnt = 0;
  448. stk_t stk;
  449. if (agnnodes(g) == 0)
  450. return 1;
  451. initStk(&stk, NULL, markFn);
  452. for (n = agfstnode(g); n; n = agnxtnode(g, n))
  453. unmark(&stk, n);
  454. n = agfstnode(g);
  455. cnt = dfs(g, agfstnode(g), NULL, &stk);
  456. freeStk(&stk);
  457. if (cnt != (size_t)agnnodes(g))
  458. ret = 0;
  459. return ret;
  460. }