mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-22 21:32:31 +00:00
Initial import
This commit is contained in:
commit
c91b3c5006
14915 changed files with 590219 additions and 0 deletions
183
libc/alg/tarjan.c
Normal file
183
libc/alg/tarjan.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ This program is free software; you can redistribute it and/or modify │
|
||||
│ it under the terms of the GNU General Public License as published by │
|
||||
│ the Free Software Foundation; version 2 of the License. │
|
||||
│ │
|
||||
│ This program is distributed in the hope that it will be useful, but │
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||||
│ General Public License for more details. │
|
||||
│ │
|
||||
│ You should have received a copy of the GNU General Public License │
|
||||
│ along with this program; if not, write to the Free Software │
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/alg/alg.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/safemacros.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Tarjan's Strongly Connected Components Algorithm.
|
||||
*
|
||||
* “The data structures that [Tarjan] devised for this problem fit
|
||||
* together in an amazingly beautiful way, so that the quantities
|
||||
* you need to look at while exploring a directed graph are always
|
||||
* magically at your fingertips. And his algorithm also does
|
||||
* topological sorting as a byproduct.” ──D.E. Knuth
|
||||
*/
|
||||
|
||||
struct Vertex {
|
||||
uint32_t Vi;
|
||||
uint32_t Ei;
|
||||
uint32_t index;
|
||||
uint32_t lowlink;
|
||||
bool onstack;
|
||||
bool selfreferential;
|
||||
};
|
||||
|
||||
struct TarjanStack {
|
||||
size_t i;
|
||||
size_t n;
|
||||
uint32_t *p;
|
||||
};
|
||||
|
||||
struct Tarjan {
|
||||
uint32_t Vn;
|
||||
uint32_t En;
|
||||
struct Vertex *V;
|
||||
const uint32_t (*E)[2];
|
||||
uint32_t *R;
|
||||
uint32_t *C;
|
||||
uint32_t Ci;
|
||||
uint32_t Ri;
|
||||
uint32_t index;
|
||||
struct TarjanStack S;
|
||||
};
|
||||
|
||||
static uint32_t TarjanPush(struct TarjanStack *st, uint32_t Vi) {
|
||||
if (st->i < st->n || grow(&st->p, &st->n, sizeof(uint32_t), 0)) {
|
||||
return (st->p[st->i++] = Vi);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t TarjanPop(struct TarjanStack *st) {
|
||||
assert(st->i != 0);
|
||||
return st->p[--st->i];
|
||||
}
|
||||
|
||||
static int TarjanConnect(struct Tarjan **tj, uint32_t Vi) {
|
||||
struct Vertex *v = &(*tj)->V[Vi];
|
||||
v->index = (*tj)->index;
|
||||
v->lowlink = (*tj)->index;
|
||||
v->onstack = true;
|
||||
(*tj)->index++;
|
||||
if (TarjanPush(&(*tj)->S, Vi) == -1) return -1;
|
||||
uint32_t fs = (*tj)->V[Vi].Ei;
|
||||
if (fs != -1) {
|
||||
for (uint32_t Ei = fs; Ei < (*tj)->En && Vi == (*tj)->E[Ei][0]; ++Ei) {
|
||||
struct Vertex *w = &(*tj)->V[(*tj)->E[Ei][1]];
|
||||
if (!w->index) {
|
||||
if (TarjanConnect(tj, w->Vi) == -1) return -1;
|
||||
v->lowlink = min(v->lowlink, w->lowlink);
|
||||
} else if (w->onstack) {
|
||||
v->lowlink = min(v->lowlink, w->index);
|
||||
}
|
||||
if (w == v) {
|
||||
w->selfreferential = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v->lowlink == v->index) {
|
||||
struct Vertex *w;
|
||||
do {
|
||||
w = &(*tj)->V[TarjanPop(&(*tj)->S)];
|
||||
w->onstack = false;
|
||||
(*tj)->R[(*tj)->Ri++] = w->Vi;
|
||||
} while (w != v);
|
||||
if ((*tj)->C) (*tj)->C[(*tj)->Ci++] = (*tj)->Ri;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines order of things in network and finds tangled clusters too.
|
||||
*
|
||||
* @param vertices is an array of vertex values, which isn't passed to
|
||||
* this function, since the algorithm only needs to consider indices
|
||||
* @param vertex_count is the number of items in the vertices array
|
||||
* @param edges are grouped directed links between indices of vertices,
|
||||
* which can be thought of as "edge[i][0] depends on edge[i][1]" or
|
||||
* "edge[i][1] must come before edge[i][0]" in topological order
|
||||
* @param edge_count is the number of items in edges, which may be 0 if
|
||||
* there aren't any connections between vertices in the graph
|
||||
* @param out_sorted receives indices into the vertices array in
|
||||
* topologically sorted order, and must be able to store
|
||||
* vertex_count items, and that's always how many are stored
|
||||
* @param out_opt_components receives indices into the out_sorted array,
|
||||
* indicating where each strongly-connected component ends; must be
|
||||
* able to store vertex_count items; and it may be NULL
|
||||
* @param out_opt_componentcount receives the number of cycle indices
|
||||
* written to out_opt_components, which will be vertex_count if
|
||||
* there aren't any cycles in the graph; and may be NULL if
|
||||
* out_opt_components is NULL
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @error ENOMEM
|
||||
* @note Tarjan's Algorithm is O(|V|+|E|)
|
||||
*/
|
||||
int tarjan(uint32_t vertex_count, const uint32_t (*edges)[2],
|
||||
uint32_t edge_count, uint32_t out_sorted[],
|
||||
uint32_t out_opt_components[], uint32_t *out_opt_componentcount) {
|
||||
assert(edge_count <= INT_MAX);
|
||||
assert(vertex_count <= INT_MAX);
|
||||
for (unsigned i = 0; i < edge_count; ++i) {
|
||||
if (i) assert(edges[i - 1][0] <= edges[i][0]);
|
||||
assert(edges[i][0] < vertex_count);
|
||||
assert(edges[i][1] < vertex_count);
|
||||
}
|
||||
int rc;
|
||||
struct Tarjan *tj;
|
||||
if ((tj = calloc(1, (sizeof(struct Tarjan) +
|
||||
sizeof(struct Vertex) * vertex_count)))) {
|
||||
tj->V = (struct Vertex *)((char *)tj + sizeof(struct Tarjan));
|
||||
tj->Vn = vertex_count;
|
||||
tj->E = edges;
|
||||
tj->En = edge_count;
|
||||
tj->R = out_sorted;
|
||||
tj->C = out_opt_components;
|
||||
tj->index = 1;
|
||||
uint32_t Vi, Ei;
|
||||
for (Vi = 0; Vi < tj->Vn; ++Vi) {
|
||||
tj->V[Vi].Vi = Vi;
|
||||
tj->V[Vi].Ei = -1u;
|
||||
}
|
||||
for (Ei = 0, Vi = -1u; Ei < tj->En; ++Ei) {
|
||||
if (tj->E[Ei][0] == Vi) continue;
|
||||
Vi = tj->E[Ei][0];
|
||||
tj->V[Vi].Ei = Ei;
|
||||
}
|
||||
rc = 0;
|
||||
for (Vi = 0; Vi < tj->Vn; ++Vi) {
|
||||
if (!tj->V[Vi].index) {
|
||||
if ((rc = TarjanConnect(&tj, Vi)) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(tj->S.p);
|
||||
assert(tj->Ri == vertex_count);
|
||||
if (out_opt_components) *out_opt_componentcount = tj->Ci;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
free(tj);
|
||||
return rc;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue