Expel Tarjan from his nest

- Nested functions are a gcc-extension and very non-portable. Refactor
  to eliminate the nesting by passing the "global" variables via
  a struct from detectSCCs().
This commit is contained in:
Panu Matilainen 2010-09-17 09:03:34 +03:00
parent 8abe568e00
commit b9168b6735
1 changed files with 89 additions and 82 deletions

View File

@ -305,100 +305,107 @@ static void addQ(tsortInfo p, tsortInfo * qp, tsortInfo * rp,
}
}
typedef struct sccData_s {
int index; /* DFS node number counter */
tsortInfo *stack; /* Stack of nodes */
int stackcnt; /* Stack top counter */
scc SCCs; /* Array of SCC's found */
int sccCnt; /* Number of SCC's found */
} * sccData;
static void tarjan(sccData sd, tsortInfo tsi) {
tsortInfo tsi_q;
relation rel;
/* use negative index numbers */
sd->index--;
/* Set the depth index for p */
tsi->tsi_SccIdx = sd->index;
tsi->tsi_SccLowlink = sd->index;
sd->stack[sd->stackcnt++] = tsi; /* Push p on the stack */
for (rel=tsi->tsi_relations; rel != NULL; rel=rel->rel_next) {
/* Consider successors of p */
tsi_q = rel->rel_suc;
if (tsi_q->tsi_SccIdx > 0)
/* Ignore already found SCCs */
continue;
if (tsi_q->tsi_SccIdx == 0){
/* Was successor q not yet visited? */
tarjan(sd, tsi_q); /* Recurse */
/* negative index numers: use max as it is closer to 0 */
tsi->tsi_SccLowlink = (
tsi->tsi_SccLowlink > tsi_q->tsi_SccLowlink
? tsi->tsi_SccLowlink : tsi_q->tsi_SccLowlink);
} else {
tsi->tsi_SccLowlink = (
tsi->tsi_SccLowlink > tsi_q->tsi_SccIdx
? tsi->tsi_SccLowlink : tsi_q->tsi_SccIdx);
}
}
if (tsi->tsi_SccLowlink == tsi->tsi_SccIdx) {
/* v is the root of an SCC? */
if (sd->stack[sd->stackcnt-1] == tsi) {
/* ignore trivial SCCs */
tsi_q = sd->stack[--sd->stackcnt];
tsi_q->tsi_SccIdx = 1;
} else {
int stackIdx = sd->stackcnt;
do {
tsi_q = sd->stack[--stackIdx];
tsi_q->tsi_SccIdx = sd->sccCnt;
} while (tsi_q != tsi);
stackIdx = sd->stackcnt;
do {
tsi_q = sd->stack[--stackIdx];
/* Calculate count for the SCC */
sd->SCCs[sd->sccCnt].count += tsi_q->tsi_count;
/* Subtract internal relations */
for (rel=tsi_q->tsi_relations; rel != NULL;
rel=rel->rel_next) {
if (rel->rel_suc != tsi_q &&
rel->rel_suc->tsi_SccIdx == sd->sccCnt)
sd->SCCs[sd->sccCnt].count--;
}
} while (tsi_q != tsi);
sd->SCCs[sd->sccCnt].size = sd->stackcnt - stackIdx;
/* copy members */
sd->SCCs[sd->sccCnt].members = xcalloc(sd->SCCs[sd->sccCnt].size,
sizeof(tsortInfo));
memcpy(sd->SCCs[sd->sccCnt].members, sd->stack + stackIdx,
sd->SCCs[sd->sccCnt].size * sizeof(tsortInfo));
sd->stackcnt = stackIdx;
sd->sccCnt++;
}
}
}
/* Search for SCCs and return an array last entry has a .size of 0 */
static scc detectSCCs(tsortInfo orderInfo, int nelem, int debugloops)
{
int index = 0; /* DFS node number counter */
tsortInfo stack[nelem]; /* An empty stack of nodes */
int stackcnt = 0;
int sccCnt = 2;
scc SCCs = xcalloc(nelem+3, sizeof(struct scc_s));
auto void tarjan(tsortInfo tsi);
void tarjan(tsortInfo tsi) {
tsortInfo tsi_q;
relation rel;
/* use negative index numbers */
index--;
/* Set the depth index for p */
tsi->tsi_SccIdx = index;
tsi->tsi_SccLowlink = index;
stack[stackcnt++] = tsi; /* Push p on the stack */
for (rel=tsi->tsi_relations; rel != NULL; rel=rel->rel_next) {
/* Consider successors of p */
tsi_q = rel->rel_suc;
if (tsi_q->tsi_SccIdx > 0)
/* Ignore already found SCCs */
continue;
if (tsi_q->tsi_SccIdx == 0){
/* Was successor q not yet visited? */
tarjan(tsi_q); /* Recurse */
/* negative index numers: use max as it is closer to 0 */
tsi->tsi_SccLowlink = (
tsi->tsi_SccLowlink > tsi_q->tsi_SccLowlink
? tsi->tsi_SccLowlink : tsi_q->tsi_SccLowlink);
} else {
tsi->tsi_SccLowlink = (
tsi->tsi_SccLowlink > tsi_q->tsi_SccIdx
? tsi->tsi_SccLowlink : tsi_q->tsi_SccIdx);
}
}
if (tsi->tsi_SccLowlink == tsi->tsi_SccIdx) {
/* v is the root of an SCC? */
if (stack[stackcnt-1] == tsi) {
/* ignore trivial SCCs */
tsi_q = stack[--stackcnt];
tsi_q->tsi_SccIdx = 1;
} else {
int stackIdx = stackcnt;
do {
tsi_q = stack[--stackIdx];
tsi_q->tsi_SccIdx = sccCnt;
} while (tsi_q != tsi);
stackIdx = stackcnt;
do {
tsi_q = stack[--stackIdx];
/* Calculate count for the SCC */
SCCs[sccCnt].count += tsi_q->tsi_count;
/* Subtract internal relations */
for (rel=tsi_q->tsi_relations; rel != NULL;
rel=rel->rel_next) {
if (rel->rel_suc != tsi_q &&
rel->rel_suc->tsi_SccIdx == sccCnt)
SCCs[sccCnt].count--;
}
} while (tsi_q != tsi);
SCCs[sccCnt].size = stackcnt - stackIdx;
/* copy members */
SCCs[sccCnt].members = xcalloc(SCCs[sccCnt].size,
sizeof(tsortInfo));
memcpy(SCCs[sccCnt].members, stack + stackIdx,
SCCs[sccCnt].size * sizeof(tsortInfo));
stackcnt = stackIdx;
sccCnt++;
}
}
}
/* Set up data structures needed for the tarjan algorithm */
scc SCCs = xcalloc(nelem+3, sizeof(*SCCs));
tsortInfo *stack = xcalloc(nelem, sizeof(*stack));
struct sccData_s sd = { 0, stack, 0, SCCs, 2 };
for (int i = 0; i < nelem; i++) {
tsortInfo tsi = &orderInfo[i];
/* Start a DFS at each node */
if (tsi->tsi_SccIdx == 0)
tarjan(tsi);
tarjan(&sd, tsi);
}
SCCs = xrealloc(SCCs, (sccCnt+1)*sizeof(struct scc_s));
free(stack);
SCCs = xrealloc(SCCs, (sd.sccCnt+1)*sizeof(struct scc_s));
/* Debug output */
if (sccCnt > 2) {
if (sd.sccCnt > 2) {
int msglvl = debugloops ? RPMLOG_WARNING : RPMLOG_DEBUG;
rpmlog(msglvl, "%i Strongly Connected Components\n", sccCnt-2);
for (int i = 2; i < sccCnt; i++) {
rpmlog(msglvl, "%i Strongly Connected Components\n", sd.sccCnt-2);
for (int i = 2; i < sd.sccCnt; i++) {
rpmlog(msglvl, "SCC #%i: requires %i packages\n",
i, SCCs[i].count);
/* loop over members */