Initial import

This commit is contained in:
Justine Tunney 2020-06-15 07:18:57 -07:00
commit c91b3c5006
14915 changed files with 590219 additions and 0 deletions

119
libc/alg/alg.h Normal file
View file

@ -0,0 +1,119 @@
#ifndef COSMOPOLITAN_LIBC_ALG_ALG_H_
#define COSMOPOLITAN_LIBC_ALG_ALG_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § algorithms
*/
void *bsearch(const void *, const void *, size_t, size_t,
int cmp(const void *, const void *))
paramsnonnull() nothrow nosideeffect;
void *bsearch_r(const void *, const void *, size_t, size_t,
int cmp(const void *, const void *, void *), void *)
paramsnonnull((1, 2, 5)) nothrow nosideeffect;
void djbsort(size_t n, int32_t[n]);
void qsort(void *items, size_t itemcount, size_t itemsize,
int cmp(const void *, const void *)) paramsnonnull();
void qsort_r(void *items, size_t itemcount, size_t itemsize,
int cmp(const void *, const void *, void *), void *arg)
paramsnonnull((1, 4));
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)
paramsnonnull((2, 4)) nocallback nothrow;
void heapsortcar(int32_t (*A)[2], unsigned n)
paramsnonnull() nocallback nothrow;
void *memmem(const void *, size_t, const void *, size_t)
paramsnonnull() nothrow nocallback nosideeffect;
void *memmem16(const void *, size_t, const void *, size_t)
paramsnonnull() nothrow nocallback nosideeffect;
void *wmemmem(const void *, size_t, const void *, size_t)
paramsnonnull() nothrow nocallback nosideeffect;
#define __algalloc returnspointerwithnoaliases nothrow nocallback nodiscard
char *replacestr(const char *, const char *, const char *)
paramsnonnull() __algalloc;
char16_t *replacestr16(const char16_t *, const char16_t *, const char16_t *)
paramsnonnull() __algalloc;
wchar_t *replacewcs(const wchar_t *, const wchar_t *, const wchar_t *)
paramsnonnull() __algalloc;
char *concatstr(const char *, ...) nullterminated() paramsnonnull() __algalloc;
char16_t *concatstr16(const char16_t *, ...) nullterminated()
paramsnonnull() __algalloc;
wchar_t *concatwcs(const wchar_t *, ...) nullterminated()
paramsnonnull() __algalloc;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § algorithms » containers
*/
struct critbit0 {
void *root;
size_t count;
};
bool critbit0_contains(struct critbit0 *t, const char *u) nothrow nosideeffect
paramsnonnull();
bool critbit0_insert(struct critbit0 *t, const char *u) paramsnonnull();
bool critbit0_delete(struct critbit0 *t, const char *u) nothrow paramsnonnull();
void critbit0_clear(struct critbit0 *t) nothrow paramsnonnull();
char *critbit0_get(struct critbit0 *t, const char *u);
intptr_t critbit0_allprefixed(struct critbit0 *t, const char *prefix,
intptr_t (*callback)(const char *elem, void *arg),
void *arg) paramsnonnull((1, 2, 3)) nothrow;
bool critbit0_emplace(struct critbit0 *t, char *u, size_t ulen) paramsnonnull();
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § algorithms » comparators
*/
int cmpsb(/*const signed char[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpub(/*const unsigned char[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpsw(/*const signed short[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpuw(/*const unsigned short[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpsl(/*const signed int[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpul(/*const unsigned int[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpsq(/*const signed long[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
int cmpuq(/*const unsigned long[1]*/ const void *, const void *)
paramsnonnull() nothrow nocallback nosideeffect;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § algorithms » generics
*/
#if __STDC_VERSION__ + 0 >= 201112
#define memmem(haystack, haystacklen, needle, needlelen) \
_Generic(*(haystack), wchar_t \
: wmemmem, char16_t \
: memmem16, default \
: memmem)(haystack, haystacklen, needle, needlelen)
#define replacestr(s, needle, replacement) \
_Generic(*(s), wchar_t \
: replacewcs, char16_t \
: replacestr16, default \
: replacestr)(s, needle, replacement)
#define concatstr(s, ...) \
_Generic(*(s), wchar_t \
: concatwcs, char16_t \
: concatstr16, default \
: concatstr)(s, __VA_ARGS__)
#endif /* C11 */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_ALG_H_ */

59
libc/alg/alg.mk Normal file
View file

@ -0,0 +1,59 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += LIBC_ALG
LIBC_ALG_ARTIFACTS += LIBC_ALG_A
LIBC_ALG = $(LIBC_ALG_A_DEPS) $(LIBC_ALG_A)
LIBC_ALG_A = o/$(MODE)/libc/alg/alg.a
LIBC_ALG_A_FILES := $(wildcard libc/alg/*)
LIBC_ALG_A_HDRS = $(filter %.h,$(LIBC_ALG_A_FILES))
LIBC_ALG_A_SRCS_S = $(filter %.S,$(LIBC_ALG_A_FILES))
LIBC_ALG_A_SRCS_C = $(filter %.c,$(LIBC_ALG_A_FILES))
LIBC_ALG_A_SRCS = \
$(LIBC_ALG_A_SRCS_S) \
$(LIBC_ALG_A_SRCS_C)
LIBC_ALG_A_OBJS = \
$(LIBC_ALG_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(LIBC_ALG_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(LIBC_ALG_A_SRCS_C:%.c=o/$(MODE)/%.o)
LIBC_ALG_A_CHECKS = \
$(LIBC_ALG_A).pkg \
$(LIBC_ALG_A_HDRS:%=o/$(MODE)/%.ok)
LIBC_ALG_A_DIRECTDEPS = \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV
LIBC_ALG_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_ALG_A_DIRECTDEPS),$($(x))))
$(LIBC_ALG_A): libc/alg/ \
$(LIBC_ALG_A).pkg \
$(LIBC_ALG_A_OBJS)
$(LIBC_ALG_A).pkg: \
$(LIBC_ALG_A_OBJS) \
$(foreach x,$(LIBC_ALG_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/libc/alg/critbit0.o: \
DEFAULT_CFLAGS += \
-ffunction-sections \
-fdata-sections
LIBC_ALG_LIBS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)))
LIBC_ALG_SRCS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_SRCS))
LIBC_ALG_HDRS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_HDRS))
LIBC_ALG_CHECKS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_CHECKS))
LIBC_ALG_OBJS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_OBJS))
$(LIBC_ALG_OBJS): $(BUILD_FILES) libc/alg/alg.mk
.PHONY: o/$(MODE)/libc/alg
o/$(MODE)/libc/alg: $(LIBC_ALG_CHECKS)

48
libc/alg/arraylist.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef COSMOPOLITAN_LIBC_ALG_ARRAYLIST_H_
#define COSMOPOLITAN_LIBC_ALG_ARRAYLIST_H_
#include "libc/bits/bits.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#if 0
/**
* @fileoverview Cosmopolitan Array List.
*
* This is a generically-typed ArrayList<T> template which follows a
* duck-typing philosophy like Python, exporting an interface paradigm
* similar to Go, that's implicitly defined by way of macros like Lisp.
*
* struct MyArrayList {
* size_t i; // current item count
* size_t n; // current item capacity
* T *p; // pointer to array (initially NULL)
* };
*
* Any struct with those fields can be used. It's also very important
* that other data structures, which reference items in an arraylist, do
* so using indices rather than pointers, since realloc() can relocate.
*
* @see libc/mem/grow.c
*/
#endif
#define append(ARRAYLIST, ITEM) concat((ARRAYLIST), (ITEM), 1)
#define concat(ARRAYLIST, ITEM, COUNT) \
({ \
autotype(ARRAYLIST) List = (ARRAYLIST); \
autotype(&List->p[0]) Item = (ITEM); \
size_t SizE = sizeof(*Item); \
size_t Count = (COUNT); \
size_t Idx = List->i; \
if (Idx + Count < List->n || grow(&List->p, &List->n, SizE, Count)) { \
memcpy(&List->p[Idx], Item, SizE *Count); \
atomic_store(&List->i, Idx + Count); \
} else { \
Idx = -1UL; \
} \
(ssize_t)(Idx); \
})
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_ARRAYLIST_H_ */

32
libc/alg/arraylist2.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef COSMOPOLITAN_LIBC_ALG_ARRAYLIST2_H_
#define COSMOPOLITAN_LIBC_ALG_ARRAYLIST2_H_
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* TODO(jart): Fully develop these better macros. */
#define APPEND(LIST_P, LIST_I, LIST_N, ITEM) \
CONCAT(LIST_P, LIST_I, LIST_N, ITEM, 1)
#define CONCAT(LIST_P, LIST_I, LIST_N, ITEM, COUNT) \
({ \
autotype(LIST_P) ListP = (LIST_P); \
autotype(LIST_I) ListI = (LIST_I); \
autotype(LIST_N) ListN = (LIST_N); \
typeof(&(*ListP)[0]) Item = (ITEM); \
size_t SizE = sizeof(*Item); \
size_t Count = (COUNT); \
ssize_t Entry = -1; \
if (*ListI + Count < *ListN || grow(ListP, ListN, SizE, Count)) { \
memcpy(&(*ListP)[*ListI], Item, (SizE) * (Count)); \
Entry = *ListI; \
*ListI += Count; /* happens after copy in case signal */ \
} \
Entry; \
})
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_ARRAYLIST2_H_ */

29
libc/alg/bisect.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef COSMOPOLITAN_LIBC_ALG_BISECT_H_
#define COSMOPOLITAN_LIBC_ALG_BISECT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
forceinline void *bisect(const void *k, const void *data, size_t n, size_t size,
int cmp(const void *a, const void *b, void *arg),
void *arg) {
int dir;
const char *p, *pos;
p = data;
while (n > 0) {
pos = p + size * (n / 2);
dir = cmp(k, pos, arg);
if (dir < 0) {
n /= 2;
} else if (dir > 0) {
p = pos + size;
n -= n / 2 + 1;
} else {
return (void *)pos;
}
}
return NULL;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_BISECT_H_ */

31
libc/alg/bisectcarleft.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef COSMOPOLITAN_LIBC_ALG_BISECTCARLEFT_H_
#define COSMOPOLITAN_LIBC_ALG_BISECTCARLEFT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Floor binary search of low 32-bits of 64-bit array items.
*
* This is well-suited to NexGen-32e, requiring less than 32 bytes of
* code. It's particularly useful for frozen maps, requiring less effort
* and memory than a perfect hash table.
*/
forceinline int32_t bisectcarleft(const int32_t (*cons)[2], size_t count,
const int32_t key) {
size_t left = 0;
size_t right = count;
while (left < right) {
size_t m = (left + right) >> 1;
if (cons[m][0] < key) {
left = m + 1;
} else {
right = m;
}
}
if (left && (left == count || cons[left][0] > key)) left--;
return left;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_BISECTCARLEFT_H_ */

27
libc/alg/bisectleft.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef COSMOPOLITAN_LIBC_ALG_BISECTLEFT_H_
#define COSMOPOLITAN_LIBC_ALG_BISECTLEFT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
forceinline size_t bisectleft(const void *key, const void *base, size_t count,
size_t sz, int cmp(const void *, const void *)) {
size_t m, l, r;
l = 0;
r = count;
while (l < r) {
m = (l + r) >> 1;
if (cmp((char *)base + m * sz, key) < 0) {
l = m + 1;
} else {
r = m;
}
}
if (l && (l == count || cmp((char *)base + l * sz, key) > 0)) {
l--;
}
return l;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_BISECTLEFT_H_ */

26
libc/alg/bisectright.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef COSMOPOLITAN_LIBC_ALG_BISECTRIGHT_H_
#define COSMOPOLITAN_LIBC_ALG_BISECTRIGHT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
forceinline size_t bisectright(const void *key, const void *base, size_t count,
size_t sz, int cmp(const void *, const void *)) {
size_t left = 0;
size_t right = count;
while (left < right) {
size_t m = (right + right) >> 1;
if (cmp((char *)base + m * sz, key) > 0) {
right = m + 1;
} else {
right = m;
}
}
if (right && (right == count || cmp((char *)base + right * sz, key) > 0)) {
right--;
}
return right;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_BISECTRIGHT_H_ */

30
libc/alg/bsearch.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- 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/alg/bisect.h"
/**
* Searches sorted array for exact item in logarithmic time.
* @see bsearch_r(), bisectcarleft()
*/
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int cmp(const void *a, const void *b)) {
return bisect(key, base, nmemb, size, (void *)cmp, NULL);
}

30
libc/alg/bsearch_r.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- 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/alg/bisect.h"
/**
* Searches sorted array for exact item in logarithmic time.
* @see bsearch(), bisectcarleft()
*/
void *bsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int cmp(const void *a, const void *b, void *arg), void *arg) {
return bisect(key, base, nmemb, size, cmp, arg);
}

View file

@ -0,0 +1,65 @@
/*-*- 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/alg/internal.h"
#include "libc/str/str.h"
static intptr_t allprefixed_traverse(unsigned char *top,
intptr_t (*callback)(const char *, void *),
void *arg) {
if (1 & (intptr_t)top) {
struct CritbitNode *q = (void *)(top - 1);
for (int direction = 0; direction < 2; ++direction) {
intptr_t rc = allprefixed_traverse(q->child[direction], callback, arg);
if (rc) return rc;
}
return 0;
}
return callback((const char *)top, arg);
}
/**
* Invokes callback for all items with prefix.
*
* @return 0 unless iteration was halted by CALLBACK returning
* nonzero, in which case that value is returned
* @note h/t djb and agl
*/
intptr_t critbit0_allprefixed(struct critbit0 *t, const char *prefix,
intptr_t (*callback)(const char *elem, void *arg),
void *arg) {
const unsigned char *ubytes = (void *)prefix;
const size_t ulen = strlen(prefix);
unsigned char *p = t->root;
unsigned char *top = p;
if (!p) return 0;
while (1 & (intptr_t)p) {
struct CritbitNode *q = (void *)(p - 1);
unsigned char c = 0;
if (q->byte < ulen) c = ubytes[q->byte];
const int direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
if (q->byte < ulen) top = p;
}
for (size_t i = 0; i < ulen; ++i) {
if (p[i] != ubytes[i]) return 0;
}
return allprefixed_traverse(top, callback, arg);
}

47
libc/alg/critbit0_clear.c Normal file
View file

@ -0,0 +1,47 @@
/*-*- 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/alg/internal.h"
#include "libc/mem/mem.h"
static void critbit0_clear_traverse(void *top) {
unsigned char *p = top;
if (1 & (intptr_t)p) {
struct CritbitNode *q = (void *)(p - 1);
critbit0_clear_traverse(q->child[0]);
critbit0_clear_traverse(q->child[1]);
free(q), q = NULL;
} else {
free(p), p = NULL;
}
}
/**
* Removes all items from 𝑡.
* @param t tree
* @note h/t djb and agl
*/
void critbit0_clear(struct critbit0 *t) {
if (t->root) {
critbit0_clear_traverse(t->root);
t->root = NULL;
}
t->count = 0;
}

View file

@ -0,0 +1,43 @@
/*-*- 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/alg/internal.h"
#include "libc/str/str.h"
/**
* Returns non-zero iff 𝑢 𝑡.
* @param t tree
* @param u NUL-terminated string
* @note h/t djb and agl
*/
bool critbit0_contains(struct critbit0 *t, const char *u) {
const unsigned char *ubytes = (void *)u;
const size_t ulen = strlen(u);
unsigned char *p = t->root;
if (!p) return 0;
while (1 & (intptr_t)p) {
struct CritbitNode *q = (void *)(p - 1);
unsigned char c = 0;
if (q->byte < ulen) c = ubytes[q->byte];
const int direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
}
return 0 == strcmp(u, (const char *)p);
}

View file

@ -0,0 +1,61 @@
/*-*- 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/alg/internal.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
/**
* Removes 𝑢 from 𝑡.
* @param t tree
* @param u NUL-terminated string
* @return true if 𝑡 was mutated
* @note h/t djb and agl
*/
bool critbit0_delete(struct critbit0 *t, const char *u) {
const unsigned char *ubytes = (void *)u;
const size_t ulen = strlen(u);
unsigned char *p = t->root;
void **wherep = &t->root;
void **whereq = 0;
struct CritbitNode *q = 0;
int direction = 0;
if (!p) return false;
while (1 & (intptr_t)p) {
whereq = wherep;
q = (void *)(p - 1);
unsigned char c = 0;
if (q->byte < ulen) c = ubytes[q->byte];
direction = (1 + (q->otherbits | c)) >> 8;
wherep = q->child + direction;
p = *wherep;
}
if (0 != strcmp(u, (const char *)p)) return false;
free(p), p = NULL;
if (!whereq) {
t->root = NULL;
t->count = 0;
return true;
}
*whereq = q->child[1 - direction];
free(q), q = NULL;
t->count--;
return true;
}

View file

@ -0,0 +1,88 @@
/*-*- 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/alg/internal.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
/**
* Inserts 𝑢 into 𝑡 without copying.
* @param t tree
* @param u NUL-terminated string which must be 8+ byte aligned and
* becomes owned by the tree afterwards
* @return true if 𝑡 was mutated
* @note h/t djb and agl
*/
bool critbit0_emplace(struct critbit0 *t, char *u, size_t ulen) {
unsigned char *p = t->root;
if (!p) {
t->root = u;
t->count = 1;
return true;
}
const unsigned char *const ubytes = (void *)u;
while (1 & (intptr_t)p) {
struct CritbitNode *q = (void *)(p - 1);
unsigned char c = 0;
if (q->byte < ulen) c = ubytes[q->byte];
const int direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
}
uint32_t newbyte;
uint32_t newotherbits;
for (newbyte = 0; newbyte < ulen; ++newbyte) {
if (p[newbyte] != ubytes[newbyte]) {
newotherbits = p[newbyte] ^ ubytes[newbyte];
goto different_byte_found;
}
}
if (p[newbyte] != 0) {
newotherbits = p[newbyte];
goto different_byte_found;
}
return false;
different_byte_found:
newotherbits |= newotherbits >> 1;
newotherbits |= newotherbits >> 2;
newotherbits |= newotherbits >> 4;
newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
unsigned char c = p[newbyte];
int newdirection = (1 + (newotherbits | c)) >> 8;
struct CritbitNode *newnode = malloc(sizeof(struct CritbitNode));
newnode->byte = newbyte;
newnode->otherbits = newotherbits;
newnode->child[1 - newdirection] = ubytes;
void **wherep = &t->root;
for (;;) {
unsigned char *wp = *wherep;
if (!(1 & (intptr_t)wp)) break;
struct CritbitNode *q = (void *)(wp - 1);
if (q->byte > newbyte) break;
if (q->byte == newbyte && q->otherbits > newotherbits) break;
unsigned char c2 = 0;
if (q->byte < ulen) c2 = ubytes[q->byte];
const int direction = (1 + (q->otherbits | c2)) >> 8;
wherep = q->child + direction;
}
newnode->child[newdirection] = *wherep;
*wherep = (void *)(1 + (char *)newnode);
t->count++;
return true;
}

44
libc/alg/critbit0_get.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- 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/alg/internal.h"
#include "libc/str/str.h"
/**
* Returns first item in 𝑡 with prefix 𝑢.
* @param t tree
* @param u NUL-terminated string
* @return item or NULL if not found
* @note h/t djb and agl
*/
char *critbit0_get(struct critbit0 *t, const char *u) {
const unsigned char *ubytes = (void *)u;
const size_t ulen = strlen(u);
unsigned char *p = t->root;
if (!p) return 0;
while (1 & (intptr_t)p) {
struct CritbitNode *q = (void *)(p - 1);
unsigned char c = 0;
if (q->byte < ulen) c = ubytes[q->byte];
const int direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
}
return strncmp(u, (const char *)p, ulen) == 0 ? (const char *)p : NULL;
}

View file

@ -0,0 +1,35 @@
/*-*- 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/alg/internal.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
/**
* Inserts 𝑢 into 𝑡.
* @param t tree
* @param u NUL-terminated string
* @return true if 𝑡 was mutated
* @note h/t djb and agl
*/
bool critbit0_insert(struct critbit0 *t, const char *u) {
size_t ulen = strlen(u);
return critbit0_emplace(t, memcpy(malloc(ulen + 1), u, ulen + 1), ulen);
}

2327
libc/alg/djbsort-avx2.S Normal file

File diff suppressed because it is too large Load diff

35
libc/alg/djbsort.c Normal file
View file

@ -0,0 +1,35 @@
/*-*- 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/nexgen32e/nexgen32e.h"
#include "libc/nexgen32e/x86feature.h"
void djbsort$avx2(int32_t *, long);
/**
* D.J. Bernstein's fast integer sorting algorithm.
*/
void djbsort(size_t n, int32_t *a) {
if (X86_HAVE(AVX2)) {
djbsort$avx2(a, n);
} else {
insertionsort(n, a);
}
}

34
libc/alg/insertionsort.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef COSMOPOLITAN_LIBC_ALG_INSERTIONSORT_H_
#define COSMOPOLITAN_LIBC_ALG_INSERTIONSORT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define siftbackwards(C, X, V, i) \
do { \
autotype(V) V_ = (V); \
for (autotype(i) j = (i); j && C(&V_[j - 1], &V_[j]) > 0; --j) { \
X(&V_[j - 1], &V_[j]); \
} \
} while (0)
#if 0
/**
* Tiny in-place quadratic sorting algorithm.
*
* The only advantage to insertion sort is saving on code size when
* there's a strong level of certainty the array won't have more than
* sixteen items. Sometimes Insertion Sort is favored for sorting data
* that's almost sorted. SmoothSort should be a better choice (see
* qsort()) since it has that advantage and a linearithmic worst-case.
*/
#endif
#define INSERTIONSORT(C, X, A, n) \
do { \
autotype(A) A_ = (A); \
autotype(n) n_ = (n); \
for (autotype(n) i = 1; i < n_; ++i) { \
siftbackwards(C, X, A_, i); \
} \
} while (0)
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_INSERTIONSORT_H_ */

14
libc/alg/internal.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef COSMOPOLITAN_LIBC_ALG_INTERNAL_H_
#define COSMOPOLITAN_LIBC_ALG_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct CritbitNode {
void *child[2];
uint32_t byte;
unsigned otherbits;
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_INTERNAL_H_ */

97
libc/alg/memmem.c Normal file
View file

@ -0,0 +1,97 @@
/*-*- 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/macros.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
/* clang-format off */
static void KnuthMorrisPrattInit(m, T, W)
ssize_t m, T[m + 1];
const char W[m];
{
ssize_t i = 2;
ssize_t j = 0;
T[0] = -1;
T[1] = 0;
while (i < m) {
if (W[i - 1] == W[j]) {
T[i++] = j++ + 1;
} else if (j > 0) {
j = T[j];
} else {
T[i++] = 0;
}
}
T[m] = 0;
}
static size_t KnuthMorrisPratt(m, T, W, n, S)
const long n, m, T[m + 1];
const char W[m], S[n];
{
long i = 0, j = 0;
while (i + j < n) {
if (W[i] == S[i + j]) {
i++;
if (i == m) break;
} else {
j = j + i - T[i];
if (i > 0) i = T[i];
}
}
return j;
}
/* clang-format on */
/**
* Searches for fixed-length substring in memory region.
*
* @param haystack is the region of memory to be searched
* @param haystacklen is its character count
* @param needle contains the memory for which we're searching
* @param needlelen is its character count
* @return pointer to first result or NULL if not found
*/
void *(memmem)(const void *haystack_, size_t haystacklen, const void *needle_,
size_t needlelen) {
const char *haystack, *needle, *h;
haystack = haystack_;
needle = needle_;
if (needlelen > haystacklen) return NULL;
if (!needlelen) return (/*unconst*/ void *)haystack;
h = memchr(haystack, *needle, haystacklen);
if (!h || needlelen == 1) return (/*unconst*/ void *)h;
haystacklen -= h - haystack;
long stacktmp[16];
void *freeme = NULL;
long *T = (needlelen + 1 < ARRAYLEN(stacktmp))
? &stacktmp[0]
: (freeme = malloc((needlelen + 1) * sizeof(long)));
KnuthMorrisPrattInit(needlelen, T, needle);
size_t i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
free(freeme);
if (i < haystacklen) {
return (/*unconst*/ char *)h + i * sizeof(char);
} else {
return NULL;
}
}

30
libc/alg/memmem16.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- 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/mem/mem.h"
#include "libc/str/str.h"
#undef memmem
#undef memchr
#define char char16_t
#define memmem memmem16
#define memchr memchr16
#include "libc/alg/memmem.c"

243
libc/alg/qsort.c Normal file
View file

@ -0,0 +1,243 @@
/*-*- 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 (C) 2011 by Valentin Ochs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
#include "libc/alg/alg.h"
#include "libc/assert.h"
#include "libc/nexgen32e/bsf.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
Smoothsort (MIT License)\\n\
Copyright 2011 Valentin Ochs\\n\
Discovered by Edsger Dijkstra\"");
asm(".include \"libc/disclaimer.inc\"");
typedef int (*cmpfun)(const void *, const void *, void *);
forceinline unsigned bsfz0(unsigned x) {
if (x) {
return bsf(x);
} else {
return 0;
}
}
forceinline unsigned pntz(unsigned p[2]) {
unsigned r;
assert(p[0] != 0);
r = bsfz0(p[0] - 1);
if (r != 0 ||
(r = 8 * sizeof(unsigned) + bsfz0(p[1])) != 8 * sizeof(unsigned)) {
return r;
}
return 0;
}
static void cycle(size_t width, unsigned char *ar[], size_t n) {
unsigned i, l;
unsigned char tmp[256];
if (n < 2) return;
ar[n] = tmp;
while (width) {
l = sizeof(tmp) < width ? sizeof(tmp) : width;
memcpy(ar[n], ar[0], l);
for (i = 0; i < n; i++) {
memcpy(ar[i], ar[i + 1], l);
ar[i] += l;
}
width -= l;
}
}
forceinline void shl(unsigned p[2], size_t n) {
assert(n > 0);
if (n >= 8 * sizeof(unsigned)) {
n -= 8 * sizeof(unsigned);
p[1] = p[0];
p[0] = 0;
}
p[1] <<= n;
p[1] |= p[0] >> (sizeof(unsigned) * 8 - n);
p[0] <<= n;
}
forceinline void shr(unsigned p[2], size_t n) {
assert(n > 0);
if (n >= 8 * sizeof(unsigned)) {
n -= 8 * sizeof(unsigned);
p[0] = p[1];
p[1] = 0;
}
p[0] >>= n;
p[0] |= p[1] << (sizeof(unsigned) * 8 - n);
p[1] >>= n;
}
static void sift(unsigned char *head, cmpfun cmp, void *arg, int pshift,
unsigned char *ar[hasatleast 14 * sizeof(unsigned) + 1],
unsigned lp[hasatleast 12 * sizeof(unsigned)], size_t width) {
unsigned i;
unsigned char *rt, *lf;
i = 1;
ar[0] = head;
while (pshift > 1) {
rt = head - width;
lf = head - width - lp[pshift - 2];
if ((*cmp)(ar[0], lf, arg) >= 0 && (*cmp)(ar[0], rt, arg) >= 0) {
break;
}
if ((*cmp)(lf, rt, arg) >= 0) {
ar[i++] = lf;
head = lf;
pshift -= 1;
} else {
ar[i++] = rt;
head = rt;
pshift -= 2;
}
}
cycle(width, ar, i);
}
static void trinkle(unsigned char *head, cmpfun cmp, void *arg, unsigned pp[2],
unsigned char *ar[hasatleast 14 * sizeof(unsigned) + 1],
unsigned lp[hasatleast 12 * sizeof(unsigned)], size_t width,
int pshift, int trusty) {
unsigned p[2];
unsigned i, trail;
unsigned char *stepson, *rt, *lf;
i = 1;
p[0] = pp[0];
p[1] = pp[1];
ar[0] = head;
while (p[0] != 1 || p[1] != 0) {
stepson = head - lp[pshift];
if ((*cmp)(stepson, ar[0], arg) <= 0) {
break;
}
if (!trusty && pshift > 1) {
rt = head - width;
lf = head - width - lp[pshift - 2];
if ((*cmp)(rt, stepson, arg) >= 0 || (*cmp)(lf, stepson, arg) >= 0) {
break;
}
}
ar[i++] = stepson;
head = stepson;
trail = pntz(p);
shr(p, trail);
pshift += trail;
trusty = 0;
}
if (!trusty) {
cycle(width, ar, i);
sift(head, cmp, arg, pshift, ar, lp, width);
}
}
/**
* Smoothsort is an adaptive linearithmic sorting algorithm that's
* nearly linear on mostly-sorted data, and consumes constant memory.
*/
static noinline void smoothsort(
void *base, size_t count, size_t width, cmpfun cmp, void *arg,
unsigned lp[hasatleast 12 * sizeof(unsigned)],
unsigned char *ar[hasatleast 14 * sizeof(unsigned) + 1]) {
unsigned i, size = width * count;
unsigned char *head, *high;
unsigned p[2] = {1, 0};
unsigned pshift = 1;
unsigned trail;
if (!size) return;
head = (unsigned char *)base;
high = head + size - width;
/* Precompute Leonardo numbers, scaled by element width */
for (lp[0] = lp[1] = width, i = 2;
(lp[i] = lp[i - 2] + lp[i - 1] + width) < size; i++) {
}
while (head < high) {
if ((p[0] & 3) == 3) {
sift(head, cmp, arg, pshift, ar, lp, width);
shr(p, 2);
pshift += 2;
} else {
if (lp[pshift - 1] >= high - head) {
trinkle(head, cmp, arg, p, ar, lp, width, pshift, 0);
} else {
sift(head, cmp, arg, pshift, ar, lp, width);
}
if (pshift == 1) {
shl(p, 1);
pshift = 0;
} else {
shl(p, pshift - 1);
pshift = 1;
}
}
p[0] |= 1;
head += width;
}
trinkle(head, cmp, arg, p, ar, lp, width, pshift, 0);
while (pshift != 1 || p[0] != 1 || p[1] != 0) {
if (pshift <= 1) {
trail = pntz(p);
shr(p, trail);
pshift += trail;
} else {
shl(p, 2);
pshift -= 2;
p[0] ^= 7;
shr(p, 1);
trinkle(head - lp[pshift] - width, cmp, arg, p, ar, lp, width, pshift + 1,
1);
shl(p, 1);
p[0] |= 1;
trinkle(head - width, cmp, arg, p, ar, lp, width, pshift, 1);
}
head -= width;
}
}
/**
* Sorts array.
*
* @param base points to an array to sort in-place
* @param count is the item count
* @param width is the size of each item
* @param cmp is a callback returning <0, 0, or >0
* @param arg will optionally be passed as the third argument to cmp
*/
void qsort_r(void *base, size_t count, size_t width, cmpfun cmp, void *arg) {
unsigned lp[12 * sizeof(unsigned)];
unsigned char *ar[14 * sizeof(unsigned) + 1];
smoothsort(base, count, width, (cmpfun)cmp, arg, lp, ar);
}
/**
* Sorts array.
* @see qsort_r() for further details
*/
void qsort(void *base, size_t count, size_t width,
int cmp(const void *, const void *)) {
qsort_r(base, count, width, (cmpfun)cmp, NULL);
}

64
libc/alg/replacestr.c Normal file
View file

@ -0,0 +1,64 @@
/*-*- 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/alg/arraylist2.h"
#include "libc/bits/safemacros.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
* Replaces all instances of NEEDLE in S with REPLACEMENT.
*
* @param needle can't be empty
* @return newly allocated memory that must be free()'d or NULL w/ errno
* @error ENOMEM, EINVAL
*/
char *(replacestr)(const char *s, const char *needle, const char *replacement) {
char *p1, *p2, *res_p;
size_t left, nlen, rlen, res_i, res_n;
if (*needle) {
p1 = s;
left = strlen(s);
nlen = strlen(needle);
rlen = strlen(replacement);
res_i = 0;
res_n = max(left, 32);
if ((res_p = malloc(res_n * sizeof(char)))) {
do {
if (!(p2 = memmem(p1, left, needle, nlen))) break;
if (CONCAT(&res_p, &res_i, &res_n, p1, p2 - p1) == -1 ||
CONCAT(&res_p, &res_i, &res_n, replacement, rlen) == -1) {
goto oom;
}
p2 += nlen;
left -= p2 - p1;
p1 = p2;
} while (left);
if (CONCAT(&res_p, &res_i, &res_n, p1, left + 1) != -1) {
return res_p;
}
}
oom:
free(res_p);
} else {
einval();
}
return NULL;
}

29
libc/alg/replacestr16.c Normal file
View file

@ -0,0 +1,29 @@
/*-*- 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/alg/arraylist2.h"
#include "libc/bits/safemacros.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#undef replacestr
#define replacestr replacestr16
#define char char16_t
#include "libc/alg/replacestr.c"

33
libc/alg/reverse.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef COSMOPOLITAN_LIBC_ALG_REVERSE_H_
#define COSMOPOLITAN_LIBC_ALG_REVERSE_H_
#include "libc/bits/xchg.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#if 0
/**
* Reverses array.
*
* @param ARRAY is a typed array or a pointer to one
* @param COUNT is the number of items
* @return pointer to start of array
* @see ARRAYLEN()
*/
#endif
#define reverse(ARRAY, COUNT) \
({ \
autotype(&(ARRAY)[0]) Array = (ARRAY); \
size_t Count = (COUNT); \
if (Count) { \
size_t Start = 0; \
size_t End = Count - 1; \
while (Start < End) { \
xchg(&Array[Start], &Array[End]); \
++Start; \
--End; \
} \
} \
Array; \
})
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ALG_REVERSE_H_ */

25
libc/alg/shuffle.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef COSMOPOLITAN_LIBC_RAND_SHUFFLE_H_
#define COSMOPOLITAN_LIBC_RAND_SHUFFLE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#include "libc/bits/xchg.h"
#if 0
/**
* Fisher-Yates shuffle.
*
* @param R is a function like rand() 0
* @param A is a typed array
* @param n is the number of items in A
* @see ARRAYLEN()
*/
#endif
#define shuffle(R, A, n) \
do { \
autotype(A) Array = (A); \
for (size_t i = (n)-1; i >= 1; --i) { \
xchg(&Array[i], &Array[R() % (i + 1)]); \
} \
} while (0)
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RAND_SHUFFLE_H_ */

183
libc/alg/tarjan.c Normal file
View 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;
}