cosmopolitan/libc/alg/tarjan.c
Justine Tunney f4f4caab0e Add x86_64-linux-gnu emulator
I wanted a tiny scriptable meltdown proof way to run userspace programs
and visualize how program execution impacts memory. It helps to explain
how things like Actually Portable Executable works. It can show you how
the GCC generated code is going about manipulating matrices and more. I
didn't feel fully comfortable with Qemu and Bochs because I'm not smart
enough to understand them. I wanted something like gVisor but with much
stronger levels of assurances. I wanted a single binary that'll run, on
all major operating systems with an embedded GPL barrier ZIP filesystem
that is tiny enough to transpile to JavaScript and run in browsers too.

https://justine.storage.googleapis.com/emulator625.mp4
2020-08-25 04:43:42 -07:00

184 lines
6.6 KiB
C

/*-*- 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/limits.h"
#include "libc/macros.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 Tarjan {
int Vn, En, Ci, Ri, *R, *C, index;
const int (*E)[2];
struct Vertex {
int Vi;
int Ei;
int index;
int lowlink;
bool onstack;
bool selfreferential;
} * V;
struct TarjanStack {
int i;
int n;
int *p;
} S;
};
static bool TarjanPush(struct Tarjan *t, int v) {
int *q;
assert(t->S.i >= 0);
assert(t->S.n >= 0);
assert(0 <= v && v < t->Vn);
if (t->S.i == t->S.n) {
if ((q = realloc(t->S.p, (t->S.n + (t->S.n >> 1) + 8) * sizeof(*t->S.p)))) {
t->S.p = q;
} else {
return false;
}
}
t->S.p[t->S.i++] = v;
return true;
}
static int TarjanPop(struct Tarjan *t) {
assert(t->S.i > 0);
return t->S.p[--t->S.i];
}
static bool TarjanConnect(struct Tarjan *t, int v) {
int fs, w, e;
assert(0 <= v && v < t->Vn);
t->V[v].index = t->index;
t->V[v].lowlink = t->index;
t->V[v].onstack = true;
t->index++;
if (!TarjanPush(t, v)) return false;
fs = t->V[v].Ei;
if (fs != -1) {
for (e = fs; e < t->En && v == t->E[e][0]; ++e) {
w = t->E[e][1];
if (!t->V[w].index) {
if (!TarjanConnect(t, t->V[w].Vi)) return false;
t->V[v].lowlink = MIN(t->V[v].lowlink, t->V[w].lowlink);
} else if (t->V[w].onstack) {
t->V[v].lowlink = MIN(t->V[v].lowlink, t->V[w].index);
}
if (w == v) {
t->V[w].selfreferential = true;
}
}
}
if (t->V[v].lowlink == t->V[v].index) {
do {
w = TarjanPop(t);
t->V[w].onstack = false;
t->R[t->Ri++] = t->V[w].Vi;
} while (w != v);
if (t->C) t->C[t->Ci++] = t->Ri;
}
return true;
}
/**
* 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(int vertex_count, const int (*edges)[2], int edge_count,
int out_sorted[], int out_opt_components[],
int *out_opt_componentcount) {
int i, rc, v, e;
struct Tarjan *t;
assert(0 <= edge_count && edge_count <= INT_MAX);
assert(0 <= vertex_count && vertex_count <= INT_MAX);
for (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);
}
if (!(t = calloc(1, (sizeof(struct Tarjan) +
sizeof(struct Vertex) * vertex_count)))) {
return -1;
}
t->V = (struct Vertex *)((char *)t + sizeof(struct Tarjan));
t->Vn = vertex_count;
t->E = edges;
t->En = edge_count;
t->R = out_sorted;
t->C = out_opt_components;
t->index = 1;
for (v = 0; v < t->Vn; ++v) {
t->V[v].Vi = v;
t->V[v].Ei = -1;
}
for (e = 0, v = -1; e < t->En; ++e) {
if (t->E[e][0] == v) continue;
v = t->E[e][0];
t->V[v].Ei = e;
}
rc = 0;
for (v = 0; v < t->Vn; ++v) {
if (!t->V[v].index) {
if (!TarjanConnect(t, v)) {
free(t->S.p);
free(t);
return -1;
}
}
}
if (out_opt_components) {
*out_opt_componentcount = t->Ci;
}
assert(t->Ri == vertex_count);
free(t->S.p);
free(t);
return rc;
}