123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*************************************************************************
- * 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/cgraph.h>
- #include <cgraph/gv_ctype.h>
- #include <cgraph/list.h>
- #include <common/render.h>
- #include <common/utils.h>
- #include <limits.h>
- #include <pack/pack.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <util/agxbuf.h>
- #include <util/alloc.h>
- #include <util/prisize_t.h>
- DEFINE_LIST(node_stack, Agnode_t *)
- typedef struct {
- node_stack_t data;
- void (*actionfn)(Agnode_t *, void *);
- bool (*markfn)(Agnode_t *, int);
- } stk_t;
- /// does `n` have a mark set?
- static bool marked(const stk_t *stk, Agnode_t *n) { return stk->markfn(n, -1); }
- /// set a mark on `n`
- static void mark(const stk_t *stk, Agnode_t *n) { stk->markfn(n, 1); }
- /// unset a mark on `n`
- static void unmark(const stk_t *stk, Agnode_t *n) { stk->markfn(n, 0); }
- static void initStk(stk_t *sp, void (*actionfn)(Agnode_t *, void *),
- bool (*markfn)(Agnode_t *, int)) {
- sp->data = (node_stack_t){0};
- sp->actionfn = actionfn;
- sp->markfn = markfn;
- }
- static void freeStk(stk_t *sp) { node_stack_free(&sp->data); }
- static void push(stk_t *sp, Agnode_t *np) {
- mark(sp, np);
- node_stack_push_back(&sp->data, np);
- }
- static Agnode_t *pop(stk_t *sp) {
- if (node_stack_is_empty(&sp->data)) {
- return NULL;
- }
- return node_stack_pop_back(&sp->data);
- }
- static size_t dfs(Agraph_t *g, Agnode_t *n, void *state, stk_t *stk) {
- Agedge_t *e;
- Agnode_t *other;
- size_t cnt = 0;
- push(stk, n);
- while ((n = pop(stk))) {
- cnt++;
- if (stk->actionfn)
- stk->actionfn(n, state);
- for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
- if ((other = agtail(e)) == n)
- other = aghead(e);
- if (!marked(stk, other))
- push(stk, other);
- }
- }
- return cnt;
- }
- static bool isLegal(const char *p) {
- char c;
- while ((c = *p++)) {
- if (c != '_' && !gv_isalnum(c))
- return false;
- }
- return true;
- }
- static void insertFn(Agnode_t *n, void *state) { agsubnode(state, n, 1); }
- static bool markFn(Agnode_t *n, int v) {
- if (v < 0)
- return ND_mark(n) != 0;
- const size_t ret = ND_mark(n);
- ND_mark(n) = v != 0;
- return ret != 0;
- }
- static void setPrefix(agxbuf *xb, const char *pfx) {
- if (!pfx || !isLegal(pfx)) {
- pfx = "_cc_";
- }
- agxbput(xb, pfx);
- }
- DEFINE_LIST(Agraphs, Agraph_t *)
- /* pccomps:
- * Return an array of subgraphs consisting of the connected
- * components of graph g. The number of components is returned in ncc.
- * All pinned nodes are in one component.
- * If pfx is non-null and a legal graph name, we use it as the prefix
- * for the name of the subgraphs created. If not, a simple default is used.
- * If pinned is non-null, *pinned set to 1 if pinned nodes found
- * and the first component is the one containing the pinned nodes.
- * Note that the component subgraphs do not contain any edges. These must
- * be obtained from the root graph.
- * Return NULL if graph is empty.
- */
- Agraph_t **pccomps(Agraph_t *g, size_t *ncc, char *pfx, bool *pinned) {
- agxbuf name = {0};
- Agraph_t *out = NULL;
- Agnode_t *n;
- bool pin = false;
- stk_t stk;
- if (agnnodes(g) == 0) {
- *ncc = 0;
- return NULL;
- }
- Agraphs_t ccs = {0};
- initStk(&stk, insertFn, markFn);
- for (n = agfstnode(g); n; n = agnxtnode(g, n))
- unmark(&stk, n);
- /* Component with pinned nodes */
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (marked(&stk, n) || !isPinned(n))
- continue;
- if (!out) {
- setPrefix(&name, pfx);
- agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
- out = agsubg(g, agxbuse(&name), 1);
- agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t),
- true); // node custom data
- Agraphs_append(&ccs, out);
- pin = true;
- }
- dfs(g, n, out, &stk);
- }
- /* Remaining nodes */
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (marked(&stk, n))
- continue;
- setPrefix(&name, pfx);
- agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
- out = agsubg(g, agxbuse(&name), 1);
- agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t),
- true); // node custom data
- dfs(g, n, out, &stk);
- Agraphs_append(&ccs, out);
- }
- freeStk(&stk);
- agxbfree(&name);
- *ncc = Agraphs_size(&ccs);
- *pinned = pin;
- return Agraphs_detach(&ccs);
- }
- /* ccomps:
- * Return an array of subgraphs consisting of the connected
- * components of graph g. The number of components is returned in ncc.
- * If pfx is non-null and a legal graph name, we use it as the prefix
- * for the name of the subgraphs created. If not, a simple default is used.
- * Note that the component subgraphs do not contain any edges. These must
- * be obtained from the root graph.
- * Returns NULL on error or if graph is empty.
- */
- Agraph_t **ccomps(Agraph_t *g, size_t *ncc, char *pfx) {
- agxbuf name = {0};
- Agraph_t *out;
- Agnode_t *n;
- stk_t stk;
- if (agnnodes(g) == 0) {
- *ncc = 0;
- return NULL;
- }
- Agraphs_t ccs = {0};
- initStk(&stk, insertFn, markFn);
- for (n = agfstnode(g); n; n = agnxtnode(g, n))
- unmark(&stk, n);
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (marked(&stk, n))
- continue;
- setPrefix(&name, pfx);
- agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
- out = agsubg(g, agxbuse(&name), 1);
- agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t),
- true); // node custom data
- dfs(g, n, out, &stk);
- Agraphs_append(&ccs, out);
- }
- freeStk(&stk);
- agxbfree(&name);
- *ncc = Agraphs_size(&ccs);
- return Agraphs_detach(&ccs);
- }
- typedef struct {
- Agrec_t h;
- char cc_subg; /* true iff subgraph corresponds to a component */
- } ccgraphinfo_t;
- typedef struct {
- Agrec_t h;
- char mark;
- union {
- Agraph_t *g;
- Agnode_t *n;
- void *v;
- } ptr;
- } ccgnodeinfo_t;
- #define GRECNAME "ccgraphinfo"
- #define NRECNAME "ccgnodeinfo"
- #define GD_cc_subg(g) (((ccgraphinfo_t *)aggetrec(g, GRECNAME, 0))->cc_subg)
- #ifdef DEBUG
- Agnode_t *dnodeOf(Agnode_t *v) {
- ccgnodeinfo_t *ip = (ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0);
- if (ip)
- return ip->ptr.n;
- fprintf(stderr, "nodeinfo undefined\n");
- return NULL;
- }
- void dnodeSet(Agnode_t *v, Agnode_t *n) {
- ((ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0))->ptr.n = n;
- }
- #else
- #define dnodeOf(v) (((ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0))->ptr.n)
- #define dnodeSet(v, w) (((ccgnodeinfo_t *)aggetrec(v, NRECNAME, 0))->ptr.n = w)
- #endif
- #define ptrOf(np) (((ccgnodeinfo_t *)((np)->base.data))->ptr.v)
- #define nodeOf(np) (((ccgnodeinfo_t *)((np)->base.data))->ptr.n)
- #define clustOf(np) (((ccgnodeinfo_t *)((np)->base.data))->ptr.g)
- #define clMark(n) (((ccgnodeinfo_t *)(n->base.data))->mark)
- /* deriveClusters:
- * Construct nodes in derived graph corresponding top-level clusters.
- * Since a cluster might be wrapped in a subgraph, we need to traverse
- * down into the tree of subgraphs
- */
- static void deriveClusters(Agraph_t *dg, Agraph_t *g) {
- Agraph_t *subg;
- Agnode_t *dn;
- Agnode_t *n;
- for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
- if (is_a_cluster(subg)) {
- dn = agnode(dg, agnameof(subg), 1);
- agbindrec(dn, NRECNAME, sizeof(ccgnodeinfo_t), true);
- clustOf(dn) = subg;
- for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
- if (dnodeOf(n)) {
- fprintf(stderr,
- "Error: node \"%s\" belongs to two non-nested clusters "
- "\"%s\" and \"%s\"\n",
- agnameof(n), agnameof(subg), agnameof(dnodeOf(n)));
- }
- dnodeSet(n, dn);
- }
- } else {
- deriveClusters(dg, subg);
- }
- }
- }
- /* deriveGraph:
- * Create derived graph dg of g where nodes correspond to top-level nodes
- * or clusters, and there is an edge in dg if there is an edge in g
- * between any nodes in the respective clusters.
- */
- static Agraph_t *deriveGraph(Agraph_t *g) {
- Agraph_t *dg;
- Agnode_t *dn;
- Agnode_t *n;
- dg = agopen("dg", Agstrictundirected, NULL);
- deriveClusters(dg, g);
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (dnodeOf(n))
- continue;
- dn = agnode(dg, agnameof(n), 1);
- agbindrec(dn, NRECNAME, sizeof(ccgnodeinfo_t), true);
- nodeOf(dn) = n;
- dnodeSet(n, dn);
- }
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- Agedge_t *e;
- Agnode_t *hd;
- Agnode_t *tl = dnodeOf(n);
- for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
- hd = aghead(e);
- hd = dnodeOf(hd);
- if (hd == tl)
- continue;
- if (hd > tl)
- agedge(dg, tl, hd, NULL, 1);
- else
- agedge(dg, hd, tl, NULL, 1);
- }
- }
- return dg;
- }
- /* unionNodes:
- * Add all nodes in cluster nodes of dg to g
- */
- static void unionNodes(Agraph_t *dg, Agraph_t *g) {
- Agnode_t *n;
- Agnode_t *dn;
- Agraph_t *clust;
- for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
- if (AGTYPE(ptrOf(dn)) == AGNODE) {
- agsubnode(g, nodeOf(dn), 1);
- } else {
- clust = clustOf(dn);
- for (n = agfstnode(clust); n; n = agnxtnode(clust, n))
- agsubnode(g, n, 1);
- }
- }
- }
- static bool clMarkFn(Agnode_t *n, int v) {
- int ret;
- if (v < 0)
- return clMark(n) != 0;
- ret = clMark(n);
- clMark(n) = (char)v;
- return ret != 0;
- }
- typedef struct {
- Agrec_t h;
- Agraph_t *orig;
- } orig_t;
- #define ORIG_REC "orig"
- Agraph_t *mapClust(Agraph_t *cl) {
- orig_t *op = (orig_t *)aggetrec(cl, ORIG_REC, 0);
- assert(op);
- return op->orig;
- }
- /* projectG:
- * If any nodes of subg are in g, create a subgraph of g
- * and fill it with all nodes of subg in g and their induced
- * edges in subg. Copy the attributes of subg to g. Return the subgraph.
- * If not, return null.
- * If subg is a cluster, the new subgraph will contain a pointer to it
- * in the record "orig".
- */
- static Agraph_t *projectG(Agraph_t *subg, Agraph_t *g, int inCluster) {
- Agraph_t *proj = NULL;
- Agnode_t *n;
- Agnode_t *m;
- orig_t *op;
- for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
- if ((m = agfindnode(g, agnameof(n)))) {
- if (proj == NULL) {
- proj = agsubg(g, agnameof(subg), 1);
- }
- agsubnode(proj, m, 1);
- }
- }
- if (!proj && inCluster) {
- proj = agsubg(g, agnameof(subg), 1);
- }
- if (proj) {
- (void)graphviz_node_induce(proj, subg);
- agcopyattr(subg, proj);
- if (is_a_cluster(proj)) {
- op = agbindrec(proj, ORIG_REC, sizeof(orig_t), false);
- op->orig = subg;
- }
- }
- return proj;
- }
- /* subgInduce:
- * Project subgraphs of root graph on subgraph.
- * If non-empty, add to subgraph.
- */
- static void subgInduce(Agraph_t *root, Agraph_t *g, int inCluster) {
- Agraph_t *subg;
- Agraph_t *proj;
- int in_cluster;
- for (subg = agfstsubg(root); subg; subg = agnxtsubg(subg)) {
- if (GD_cc_subg(subg))
- continue;
- if ((proj = projectG(subg, g, inCluster))) {
- in_cluster = (inCluster || is_a_cluster(subg));
- subgInduce(subg, proj, in_cluster);
- }
- }
- }
- static void subGInduce(Agraph_t *g, Agraph_t *out) { subgInduce(g, out, 0); }
- /* cccomps:
- * Decompose g into "connected" components, where nodes are connected
- * either by an edge or by being in the same cluster. The components
- * are returned in an array of subgraphs. ncc indicates how many components
- * there are. The subgraphs use the prefix pfx in their names, if non-NULL.
- * Note that cluster subgraph of the main graph, corresponding to a component,
- * is cloned within the subgraph. Each cloned cluster contains a record pointing
- * to the real cluster.
- */
- Agraph_t **cccomps(Agraph_t *g, size_t *ncc, char *pfx) {
- Agraph_t *dg;
- size_t n_cnt, e_cnt;
- agxbuf name = {0};
- Agraph_t *out;
- Agraph_t *dout;
- Agnode_t *dn;
- stk_t stk;
- int sz = (int)sizeof(ccgraphinfo_t);
- if (agnnodes(g) == 0) {
- *ncc = 0;
- return NULL;
- }
- /* Bind ccgraphinfo to graph and all subgraphs */
- aginit(g, AGRAPH, GRECNAME, -sz, false);
- /* Bind ccgraphinfo to graph and all subgraphs */
- aginit(g, AGNODE, NRECNAME, sizeof(ccgnodeinfo_t), false);
- dg = deriveGraph(g);
- size_t ccs_length = (size_t)agnnodes(dg);
- Agraphs_t ccs = {0};
- Agraphs_reserve(&ccs, ccs_length);
- initStk(&stk, insertFn, clMarkFn);
- for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
- if (marked(&stk, dn))
- continue;
- setPrefix(&name, pfx);
- agxbprint(&name, "%" PRISIZE_T, Agraphs_size(&ccs));
- char *name_str = agxbuse(&name);
- dout = agsubg(dg, name_str, 1);
- out = agsubg(g, name_str, 1);
- agbindrec(out, GRECNAME, sizeof(ccgraphinfo_t), false);
- GD_cc_subg(out) = 1;
- n_cnt = dfs(dg, dn, dout, &stk);
- unionNodes(dout, out);
- e_cnt = graphviz_node_induce(out, NULL);
- subGInduce(g, out);
- Agraphs_append(&ccs, out);
- agdelete(dg, dout);
- if (Verbose)
- fprintf(stderr,
- "(%4" PRISIZE_T ") %7" PRISIZE_T " nodes %7" PRISIZE_T " edges\n",
- Agraphs_size(&ccs) - 1, n_cnt, e_cnt);
- }
- if (Verbose)
- fprintf(stderr,
- " %7d nodes %7d edges %7" PRISIZE_T " components %s\n",
- agnnodes(g), agnedges(g), Agraphs_size(&ccs), agnameof(g));
- agclose(dg);
- agclean(g, AGRAPH, GRECNAME);
- agclean(g, AGNODE, NRECNAME);
- freeStk(&stk);
- agxbfree(&name);
- *ncc = Agraphs_size(&ccs);
- return Agraphs_detach(&ccs);
- }
- /* isConnected:
- * Returns 1 if the graph is connected.
- * Returns 0 if the graph is not connected.
- * Returns -1 if the graph is error.
- */
- int isConnected(Agraph_t *g) {
- Agnode_t *n;
- int ret = 1;
- size_t cnt = 0;
- stk_t stk;
- if (agnnodes(g) == 0)
- return 1;
- initStk(&stk, NULL, markFn);
- for (n = agfstnode(g); n; n = agnxtnode(g, n))
- unmark(&stk, n);
- n = agfstnode(g);
- cnt = dfs(g, agfstnode(g), NULL, &stk);
- freeStk(&stk);
- if (cnt != (size_t)agnnodes(g))
- ret = 0;
- return ret;
- }
|