/* gutil1.c: Some graph utilities. */ #include "gtools.h" #include "gutils.h" /**************************************************************************/ void degstats(graph *g, int m, int n, unsigned long *edges, int *mindeg, int *mincount, int *maxdeg, int *maxcount, boolean *eulerian) /* Compute degree-related graph properties. *edges = number of edges *mindeg, *mincount = minimum degree and how many there are *maxdeg, *maxcount = maximum degree and how many there are *eulerian = whether the graph has only even degrees */ { setword *pg; int i,j,d,dor; int mind,mindc,maxd,maxdc; unsigned long ned; mind = n; mindc = 0; maxd = 0; maxdc = 0; ned = 0; dor = 0; pg = (setword*)g; for (i = 0; i < n; ++i) { d = 0; for (j = 0; j < m; ++j, ++pg) if (*pg) d += POPCOUNT(*pg); if (d == mind) ++mindc; else if (d < mind) { mind = d; mindc = 1; } if (d == maxd) ++maxdc; else if (d > maxd) { maxd = d; maxdc = 1; } dor |= d; ned += d; } *mindeg = mind; *mincount = mindc; *maxdeg = maxd; *maxcount = maxdc; *edges = ned / 2; *eulerian = (dor & 1) == 0; } /**************************************************************************/ void degstats3(graph *g, int m, int n, unsigned long *edges, int *mindeg, int *mincount, int *maxdeg, int *maxcount, int *odddeg) /* Compute degree-related graph properties. *edges = number of edges *mindeg, *mincount = minimum degree and how many there are *maxdeg, *maxcount = maximum degree and how many there are *odddeg = number of vertices of odd degree */ { setword *pg; int i,j,d,dodd; int mind,mindc,maxd,maxdc; unsigned long ned; mind = n; mindc = 0; maxd = 0; maxdc = 0; ned = 0; dodd = 0; pg = (setword*)g; for (i = 0; i < n; ++i) { d = 0; for (j = 0; j < m; ++j, ++pg) if (*pg) d += POPCOUNT(*pg); if (d == mind) ++mindc; else if (d < mind) { mind = d; mindc = 1; } if (d == maxd) ++maxdc; else if (d > maxd) { maxd = d; maxdc = 1; } dodd += d % 2; ned += d; } *mindeg = mind; *mincount = mindc; *maxdeg = maxd; *maxcount = maxdc; *edges = ned / 2; *odddeg = dodd; } /**************************************************************************/ void degstats2(graph *g, boolean digraph, int m, int n, unsigned long *edges, int *loops, int *minindeg, int *minincount, int *maxindeg, int *maxincount, int *minoutdeg, int *minoutcount, int *maxoutdeg, int *maxoutcount, boolean *eulerian) /* Compute degree-related graph properties. *edges = number of edges (including loops), directed edges for digraphs *loops = number of loops *minindeg, *minincount = minimum in-degree and how many there are *maxindeg, *maxincount = maximum in-degree and how many there are *minoutdeg,*minoutcount,*maxoutdeg,*maxoutcount = similarly for out-degree *eulerian = whether the undirected graph has only even degrees, or the directed graph has indegree=outdegree at each vertex. A loop contributes 2 to the degrees of an undirected graph and 1 to each degree for a directed graph. */ { setword *pg; int i,j,d,dor; int mind,mindc,maxd,maxdc; unsigned long ned; int nloops; #if MAXN int indeg[MAXN]; int outdeg[MAXN]; #else DYNALLSTAT(int,indeg,indeg_sz); DYNALLSTAT(int,outdeg,outdeg_sz); #endif if (n == 0) { *edges = *loops = 0; *minindeg = *minincount = *maxindeg = *maxincount = 0; *minoutdeg = *minoutcount = *maxoutdeg = *maxoutcount = 0; *eulerian = TRUE; return; } #if !MAXN if (digraph) { DYNALLOC1(int,indeg,indeg_sz,n,"degstats2"); DYNALLOC1(int,outdeg,outdeg_sz,n,"degstats2"); } #endif if (!digraph) { mind = n+2; mindc = 0; maxd = 0; maxdc = 0; ned = 0; dor = 0; nloops = 0; pg = (setword*)g; for (i = 0; i < n; ++i) { d = 0; if (ISELEMENT(pg,i)) { ++d; ++nloops; } for (j = 0; j < m; ++j, ++pg) if (*pg) d += POPCOUNT(*pg); if (d == mind) ++mindc; else if (d < mind) { mind = d; mindc = 1; } if (d == maxd) ++maxdc; else if (d > maxd) { maxd = d; maxdc = 1; } dor |= d; ned += d; } *minindeg = *minoutdeg = mind; *minincount = *minoutcount = mindc; *maxindeg = *maxoutdeg = maxd; *maxincount = *maxoutcount = maxdc; *edges = ned / 2; *eulerian = (dor & 1) == 0; *loops = nloops; } else { for (i = 0; i < n; ++i) indeg[i] = outdeg[i] = 0; nloops = 0; ned = 0; for (i = 0, pg = (setword*)g; i < n; ++i, pg += m) { if (ISELEMENT(pg,i)) ++nloops; for (j = -1; (j = nextelement(pg,m,j)) >= 0;) { ++outdeg[i]; ++indeg[j]; } ned += outdeg[i]; } *edges = ned; *loops = nloops; mind = maxd = indeg[0]; mindc = maxdc = 1; for (i = 1; i < n; ++i) { d = indeg[i]; if (d == mind) ++mindc; else if (d < mind) { mind = d; mindc = 1; } if (d == maxd) ++maxdc; else if (d > maxd) { maxd = d; maxdc = 1; } } *minindeg = mind; *minincount = mindc; *maxindeg = maxd; *maxincount = maxdc; mind = maxd = outdeg[0]; mindc = maxdc = 1; for (i = 1; i < n; ++i) { d = outdeg[i]; if (d == mind) ++mindc; else if (d < mind) { mind = d; mindc = 1; } if (d == maxd) ++maxdc; else if (d > maxd) { maxd = d; maxdc = 1; } } *minoutdeg = mind; *minoutcount = mindc; *maxoutdeg = maxd; *maxoutcount = maxdc; for (i = 0; i < n; ++i) if (indeg[i] != outdeg[i]) break; *eulerian = (i == n); } } /*********************************************************************/ boolean isconnected1(graph *g, int n) /* test if g is connected (m=1) */ { setword seen,expanded,toexpand; int i; if (n == 0) return FALSE; seen = bit[0]; expanded = 0; while ((toexpand = (seen & ~expanded)) != 0) { i = FIRSTBITNZ(toexpand); expanded |= bit[i]; seen |= g[i]; } return POPCOUNT(seen) == n; } /**************************************************************************/ boolean isconnected(graph *g, int m, int n) /* Test if g is connected */ { int i,head,tail,w; set *gw; #if MAXN int queue[MAXN],visited[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); DYNALLSTAT(int,visited,visited_sz); #endif if (n == 0) return FALSE; if (m == 1) return isconnected1(g,n); #if !MAXN DYNALLOC1(int,queue,queue_sz,n,"isconnected"); DYNALLOC1(int,visited,visited_sz,n,"isconnected"); #endif for (i = 0; i < n; ++i) visited[i] = 0; queue[0] = 0; visited[0] = 1; head = 0; tail = 1; while (head < tail) { w = queue[head++]; gw = GRAPHROW(g,w,m); for (i = -1; (i = nextelement(gw,m,i)) >= 0;) { if (!visited[i]) { visited[i] = 1; queue[tail++] = i; } } } return tail == n; } /**************************************************************************/ boolean issubconnected(graph *g, set *sub, int m, int n) /* Test if the subset of g induced by sub is connected. Empty is connected. */ /* Note that the value for empty subgraphs disagrees with isconnected() */ { int i,head,tail,w,subsize; set *gw; #if MAXN int queue[MAXN],visited[MAXN]; setword subw[MAXM]; #else DYNALLSTAT(int,queue,queue_sz); DYNALLSTAT(int,visited,visited_sz); DYNALLSTAT(set,subw,subw_sz); DYNALLOC1(int,queue,queue_sz,n,"issubconnected"); DYNALLOC1(int,visited,visited_sz,n,"issubconnected"); DYNALLOC1(set,subw,subw_sz,m,"issubconnected"); #endif subsize = 0; for (i = 0; i < m; ++i) subsize += (sub[i] ? POPCOUNT(sub[i]) : 0); if (subsize <= 1) return TRUE; for (i = 0; i < n; ++i) visited[i] = 0; i = nextelement(sub,m,-1); queue[0] = i; visited[i] = 1; head = 0; tail = 1; while (head < tail) { w = queue[head++]; gw = GRAPHROW(g,w,m); for (i = 0; i < m; ++i) subw[i] = gw[i] & sub[i]; for (i = -1; (i = nextelement(subw,m,i)) >= 0;) { if (!visited[i]) { visited[i] = 1; queue[tail++] = i; } } } return tail == subsize; } /**********************************************************************/ boolean isbiconnected1(graph *g, int n) /* Test if g is biconnected; version for m=1. */ { int sp,v,w; setword sw; int numvis; setword visited; int num[WORDSIZE],lp[WORDSIZE],stack[WORDSIZE]; if (n <= 2) return FALSE; visited = bit[0]; stack[0] = 0; num[0] = 0; lp[0] = 0; numvis = 1; sp = 0; v = 0; for (;;) { if ((sw = g[v] & ~visited)) /* not "==" */ { w = v; v = FIRSTBITNZ(sw); /* visit next child */ stack[++sp] = v; visited |= bit[v]; lp[v] = num[v] = numvis++; sw = g[v] & visited & ~bit[w]; while (sw) { w = FIRSTBITNZ(sw); sw &= ~bit[w]; if (num[w] < lp[v]) lp[v] = num[w]; } } else { w = v; /* back up to parent */ if (sp <= 1) return numvis == n; v = stack[--sp]; if (lp[w] >= num[v]) return FALSE; if (lp[w] < lp[v]) lp[v] = lp[w]; } } } /**********************************************************************/ boolean isbiconnected(graph *g, int m, int n) /* test if g is biconnected */ { int sp,v,vc; int numvis; set *gv; #if MAXN int num[MAXN],lp[MAXN],stack[MAXN]; #else DYNALLSTAT(int,num,num_sz); DYNALLSTAT(int,lp,lp_sz); DYNALLSTAT(int,stack,stack_sz); #endif if (n <= 2) return FALSE; if (m == 1) return isbiconnected1(g,n); #if !MAXN DYNALLOC1(int,num,num_sz,n,"isbiconnected"); DYNALLOC1(int,lp,lp_sz,n,"isbiconnected"); DYNALLOC1(int,stack,stack_sz,n,"isbiconnected"); #endif num[0] = 0; for (v = 1; v < n; ++v) num[v] = -1; lp[0] = 0; numvis = 1; sp = 0; v = 0; vc = -1; gv = (set*)g; for (;;) { vc = nextelement(gv,m,vc); if (vc < 0) { if (sp <= 1) return numvis == n; vc = v; v = stack[--sp]; gv = GRAPHROW(g,v,m); if (lp[vc] >= num[v]) return FALSE; if (lp[vc] < lp[v]) lp[v] = lp[vc]; } else if (num[vc] < 0) { stack[++sp] = vc; v = vc; gv = GRAPHROW(g,v,m); vc = -1; lp[v] = num[v] = numvis++; } else if (vc != v) { if (num[vc] < lp[v]) lp[v] = num[vc]; } } } /**************************************************************************/ boolean twocolouring(graph *g, int *colour, int m, int n) /* If g is bipartite, set colour[*] to 0 or 1 to indicate an example of 2-colouring and return TRUE. Otherwise return FALSE. Colour 0 is assigned to the first vertex of each component. */ { int i,head,tail,v,w,need; set *gw; setword xg; #if MAXN int queue[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); #endif #if !MAXN DYNALLOC1(int,queue,queue_sz,n,"twocolouring"); #endif if (n == 0) return TRUE; for (i = 0; i < n; ++i) colour[i] = -1; if (m == 1) { for (v = 0; v < n; ++v) if (colour[v] < 0) { queue[0] = v; colour[v] = 0; head = 0; tail = 1; while (head < tail) { w = queue[head++]; need = 1 - colour[w]; xg = g[w]; while (xg) { TAKEBIT(i,xg); if (colour[i] < 0) { colour[i] = need; queue[tail++] = i; } else if (colour[i] != need) return FALSE; } } } } else { for (v = 0; v < n; ++v) if (colour[v] < 0) { queue[0] = v; colour[v] = 0; head = 0; tail = 1; while (head < tail) { w = queue[head++]; need = 1 - colour[w]; gw = GRAPHROW(g,w,m); for (i = -1; (i = nextelement(gw,m,i)) >= 0;) { if (colour[i] < 0) { colour[i] = need; queue[tail++] = i; } else if (colour[i] != need) return FALSE; } } } } return TRUE; } /**************************************************************************/ boolean isbipartite(graph *g, int m, int n) /* Test if g is bipartite */ { #if MAXN int colour[MAXN]; #else DYNALLSTAT(int,colour,colour_sz); DYNALLOC1(int,colour,colour_sz,n,"isbipartite"); #endif return twocolouring(g,colour,m,n); } /**************************************************************************/ int bipartiteside(graph *g, int m, int n) /* If g is not bipartite, return 0. Otherwise return the size of the smallest of the two parts of a 2-coluring for which this value is smallest. */ { int i,head,tail,v,w,need; set *gw; setword xg; int ans,col[2]; #if MAXN int queue[MAXN]; int colour[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); DYNALLSTAT(int,colour,colour_sz); #endif #if !MAXN DYNALLOC1(int,queue,queue_sz,n,"twocolouring"); DYNALLOC1(int,colour,colour_sz,n,"isbipartite"); #endif if (n == 0) return 0; for (i = 0; i < n; ++i) colour[i] = -1; ans = 0; if (m == 1) { for (v = 0; v < n; ++v) { if (colour[v] < 0) { queue[0] = v; colour[v] = 0; col[0] = 1; col[1] = 0; head = 0; tail = 1; while (head < tail) { w = queue[head++]; need = 1 - colour[w]; xg = g[w]; while (xg) { TAKEBIT(i,xg); if (colour[i] < 0) { colour[i] = need; ++col[need]; queue[tail++] = i; } else if (colour[i] != need) return 0; } } if (col[0] <= col[1]) ans += col[0]; else ans += col[1]; } } } else { for (v = 0; v < n; ++v) { if (colour[v] < 0) { queue[0] = v; colour[v] = 0; col[0] = 1; col[1] = 0; head = 0; tail = 1; while (head < tail) { w = queue[head++]; need = 1 - colour[w]; gw = GRAPHROW(g,w,m); for (i = -1; (i = nextelement(gw,m,i)) >= 0;) { if (colour[i] < 0) { colour[i] = need; ++col[need]; queue[tail++] = i; } else if (colour[i] != need) return 0; } } if (col[0] <= col[1]) ans += col[0]; else ans += col[1]; } } } return ans; } /**************************************************************************/ int girth(graph *g, int m, int n) /* Find the girth of graph g. 0 means acyclic. */ { int i,head,tail,v,w; int best,c,dw1; set *gw; #if MAXN int dist[MAXN],queue[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); DYNALLSTAT(int,dist,dist_sz); DYNALLOC1(int,queue,queue_sz,n,"girth"); DYNALLOC1(int,dist,dist_sz,n,"girth"); #endif if (n == 0) return 0; best = n+3; for (v = 0; v < n; ++v) { for (i = 0; i < n; ++i) dist[i] = -1; queue[0] = v; dist[v] = 0; head = 0; tail = 1; while (head < tail) { w = queue[head++]; gw = GRAPHROW(g,w,m); dw1 = dist[w] + 1; for (i = -1; (i = nextelement(gw,m,i)) >= 0;) { if (dist[i] < 0) { dist[i] = dw1; queue[tail++] = i; } else if (dist[i] >= dist[w]) { c = dw1 + dist[i]; if (c < best) best = c; if ((c & 1) != 0 || c > best) break; } } if (i >= 0) break; } if (best == 3) return 3; } return (best > n ? 0 : best); } /**************************************************************************/ void find_dist(graph *g, int m, int n, int v, int *dist) /* Put in dist[0..n-1] the distance of each vertex from v. Vertices in a different component are given the distance n. */ { int i,head,tail,w; set *gw; #if MAXN int queue[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); #endif #if !MAXN DYNALLOC1(int,queue,queue_sz,n,"isconnected"); #endif if (n == 0) return; for (i = 0; i < n; ++i) dist[i] = n; queue[0] = v; dist[v] = 0; head = 0; tail = 1; while (tail < n && head < tail) { w = queue[head++]; gw = GRAPHROW(g,w,m); for (i = -1; (i = nextelement(gw,m,i)) >= 0;) { if (dist[i] == n) { dist[i] = dist[w] + 1; queue[tail++] = i; } } } } /**************************************************************************/ void find_dist2(graph *g, int m, int n, int v, int w, int *dist) /* Put in dist[0..n-1] the distance of each vertex from {v,w}. Vertices in a different component are given the distance n. */ { int i,head,tail,x; set *gx; #if MAXN int queue[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); #endif #if !MAXN DYNALLOC1(int,queue,queue_sz,n,"isconnected"); #endif if (n == 0) return; for (i = 0; i < n; ++i) dist[i] = n; queue[0] = v; queue[1] = w; dist[v] = dist[w] = 0; head = 0; tail = 2; while (tail < n && head < tail) { x = queue[head++]; gx = GRAPHROW(g,x,m); for (i = -1; (i = nextelement(gx,m,i)) >= 0;) { if (dist[i] == n) { dist[i] = dist[x] + 1; queue[tail++] = i; } } } } /**************************************************************************/ void diamstats(graph *g, int m, int n, int *radius, int *diameter) /* Find the radius and diameter. Both -1 if g is disconnected. We use an O(mn) algorithm, which is pretty disgraceful. */ { int v,i,head,tail,w; int ecc,diam,rad; set *gw; #if MAXN int queue[MAXN],dist[MAXN]; #else DYNALLSTAT(int,queue,queue_sz); DYNALLSTAT(int,dist,dist_sz); #endif /* if (m == 1) {diamstats1(g,n,radius,diameter); return; } */ #if !MAXN DYNALLOC1(int,queue,queue_sz,n,"isconnected"); DYNALLOC1(int,dist,dist_sz,n,"isconnected"); #endif if (n == 0) { *radius = *diameter = 0; return; } diam = -1; rad = n; for (v = 0; v < n; ++v) { for (i = 0; i < n; ++i) dist[i] = -1; queue[0] = v; dist[v] = 0; head = 0; tail = 1; while (tail < n && head < tail) { w = queue[head++]; gw = GRAPHROW(g,w,m); for (i = -1; (i = nextelement(gw,m,i)) >= 0;) { if (dist[i] < 0) { dist[i] = dist[w] + 1; queue[tail++] = i; } } } if (tail < n) { *diameter = *radius = -1; return; } ecc = dist[queue[n-1]]; if (ecc > diam) diam = ecc; if (ecc < rad) rad = ecc; } *diameter = diam; *radius = rad; } /**************************************************************************/ static long maxclnode1(graph *g, setword cliq, setword cov, int maxv) /* Internal search node. cov has all the vertices outside cliq that * cover all of cliq. maxv is the last vertex of cliq. */ { long ans; int i; setword w; if (cov == 0) return 1; ans = 0; w = cov & BITMASK(maxv); while (w) { TAKEBIT(i,w); ans += maxclnode1(g,cliq|bit[i],cov&g[i]&~bit[i],i); } return ans; } long maxcliques(graph *g, int m, int n) /* Find the number of maximal cliques */ { int i; long ans; if (n == 0) return 0; if (m == 1) { ans = 0; for (i = 0; i < n; ++i) ans += maxclnode1(g,bit[i],g[i],i); } else { fprintf(stderr,">E maxcliques() is only implemented for m=1\n"); exit(1); } return ans; } /**************************************************************************/ static void maxcsnode1(int *best, graph *g, setword cliq, setword cov, int maxv) /* Internal search node. cov has all the vertices outside cliq that * cover all of cliq. maxv is the last vertex of cliq. * *best is the largest clique known so far. */ { int i,s; setword w; if (cov == 0) return; w = cov & BITMASK(maxv); s = POPCOUNT(cliq); if (s + POPCOUNT(w) <= *best) return; if (w) { if (s + 1 > *best) *best = s + 1; while (w) { TAKEBIT(i,w); maxcsnode1(best,g,cliq|bit[i],cov&g[i]&~bit[i],i); } } } int maxcliquesize(graph *g, int m, int n) /* Find the largest clique size */ { int i,best; if (n == 0) return 0; if (m == 1) { best = 1; for (i = 0; i < n; ++i) maxcsnode1(&best,g,bit[i],g[i],i); } else { fprintf(stderr,">E maxcliquesize() is only implemented for m=1\n"); exit(1); } return best; } int maxindsetsize(graph *g, int m, int n) /* Find the largest independent set size */ { int i,best; graph gc[WORDSIZE]; setword all; if (n == 0) return 0; if (m == 1) { all = ALLMASK(n); for (i = 0; i < n; ++i) gc[i] = g[i] ^ all ^ bit[i]; best = 1; for (i = 0; i < n; ++i) maxcsnode1(&best,gc,bit[i],gc[i],i); } else { fprintf(stderr,">E maxindsetsize() is only implemented for m=1\n"); exit(1); } return best; }