feat(stdlib): rand/div/qsort/bsearch
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Integer absolute value and division helpers (todo 12). The abs family
|
||||
* negates in the unsigned type, so the most-negative value wraps to
|
||||
* itself without undefined behavior and errno is never set. div computes
|
||||
* through long long intermediates, where every int quotient and
|
||||
* remainder always fits, so INT_MIN / -1 works; ldiv/lldiv special-case
|
||||
* the one overflowing pair (num == MIN && denom == -1) and otherwise
|
||||
* use plain / and %, which C23 truncates toward zero. Division by zero
|
||||
* is undefined behavior and is not handled here.
|
||||
*/
|
||||
|
||||
int
|
||||
abs(int n)
|
||||
{
|
||||
return n < 0 ? (int)(0U - (unsigned int)n) : n;
|
||||
}
|
||||
|
||||
long
|
||||
labs(long n)
|
||||
{
|
||||
return n < 0 ? (long)(0UL - (unsigned long)n) : n;
|
||||
}
|
||||
|
||||
long long
|
||||
llabs(long long n)
|
||||
{
|
||||
return n < 0 ? (long long)(0ULL - (unsigned long long)n) : n;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
div_t
|
||||
div(int num, int denom)
|
||||
{
|
||||
long long a = num;
|
||||
long long b = denom;
|
||||
div_t r;
|
||||
|
||||
r.quot = (int)(a / b);
|
||||
r.rem = (int)(a % b);
|
||||
return r;
|
||||
}
|
||||
|
||||
ldiv_t
|
||||
ldiv(long num, long denom)
|
||||
{
|
||||
ldiv_t r;
|
||||
|
||||
if (num == LONG_MIN && denom == -1L)
|
||||
{
|
||||
r.quot = LONG_MIN;
|
||||
r.rem = 0L;
|
||||
return r;
|
||||
}
|
||||
r.quot = num / denom;
|
||||
r.rem = num % denom;
|
||||
return r;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
lldiv_t
|
||||
lldiv(long long num, long long denom)
|
||||
{
|
||||
lldiv_t r;
|
||||
|
||||
if (num == LLONG_MIN && denom == -1LL)
|
||||
{
|
||||
r.quot = LLONG_MIN;
|
||||
r.rem = 0LL;
|
||||
return r;
|
||||
}
|
||||
r.quot = num / denom;
|
||||
r.rem = num % denom;
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* qsort/bsearch (todo 12). qsort is a median-of-three introsort over raw
|
||||
* bytes: quicksort with a Hoare-style partition, an insertion sort for
|
||||
* short segments, and a depth cap of 2*log2(n) past which the segment
|
||||
* falls back to heapsort, so adversarial input can never degrade to
|
||||
* O(n^2). Only the smaller partition half is recursed; the larger half
|
||||
* is iterated, bounding recursion depth by log2(n).
|
||||
*
|
||||
* Pivot selection sorts the three sample elements by value (median of
|
||||
* three) so the median sits at mid, then parks it at hi-1; the min and
|
||||
* max samples stay at lo and hi as outer sentinels. Partitioning only
|
||||
* the range [lo + 1, hi - 2], with an i-scan bounded at hi - 2 and a
|
||||
* j-scan bounded at lo, keeps the parked pivot pointer out of both
|
||||
* scans: compar is never called with two pointers to the same element.
|
||||
* Sorting the samples (rather than just locating the median) keeps the
|
||||
* pivot a true middle value on adversarial shapes such as reverse-
|
||||
* sorted data, whose first partition would otherwise hand down halves
|
||||
* shaped [local-max, ascending] that peel one element per level.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Swap size bytes between a and b, one byte at a time. The
|
||||
* no-tree-loop-distribute-patterns attribute stops GCC from recognizing
|
||||
* the byte loop as a memcpy idiom and rewriting it into a call to
|
||||
* memcpy — which for small sizes is exactly this loop, self-recursing
|
||||
* (see src/string/memcpy.c for the same guard on its byte loops).
|
||||
*/
|
||||
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
|
||||
swap_bytes(void *a, void *b, size_t size) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned char *pa = a;
|
||||
unsigned char *pb = b;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
unsigned char t = pa[i];
|
||||
|
||||
pa[i] = pb[i];
|
||||
pb[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Address of element i of an array of size-byte elements at base. */
|
||||
static unsigned char *
|
||||
elem(void *base, size_t size, size_t i)
|
||||
{
|
||||
return (unsigned char *)base + (i * size);
|
||||
}
|
||||
|
||||
/* Insertion sort: bubble each element down; width-independent, no temp
|
||||
* buffer, no VLA. */
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
static void
|
||||
insertion_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 1; i < nmemb; i++)
|
||||
{
|
||||
size_t j = i;
|
||||
|
||||
while (j > 0 && compar(elem(base, size, j), elem(base, size, j - 1)) < 0)
|
||||
{
|
||||
swap_bytes(elem(base, size, j), elem(base, size, j - 1), size);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
/* Sort the three sample elements at lo, mid, hi in place so
|
||||
* elem(lo) <= elem(mid) <= elem(hi): the median value ends up at mid.
|
||||
* The three positions are distinct (the caller guarantees >= 3
|
||||
* elements), so no comparand is ever the same element twice. */
|
||||
static void
|
||||
sort_samples(void *base, size_t size, size_t lo, size_t mid, size_t hi,
|
||||
int (*compar)(const void *, const void *))
|
||||
{
|
||||
void *a = elem(base, size, lo);
|
||||
void *b = elem(base, size, mid);
|
||||
void *c = elem(base, size, hi);
|
||||
|
||||
if (compar(b, a) < 0)
|
||||
{
|
||||
swap_bytes(b, a, size);
|
||||
}
|
||||
if (compar(c, a) < 0)
|
||||
{
|
||||
swap_bytes(c, a, size);
|
||||
}
|
||||
if (compar(c, b) < 0)
|
||||
{
|
||||
swap_bytes(c, b, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Heapsort over [base, base + nmemb*size): sift-down with root/child
|
||||
* swaps, never a self-comparison. */
|
||||
static void
|
||||
sift_down(void *base, size_t nmemb, size_t size, size_t root,
|
||||
int (*compar)(const void *, const void *))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
size_t left = (2 * root) + 1;
|
||||
size_t largest;
|
||||
|
||||
if (left >= nmemb)
|
||||
{
|
||||
return;
|
||||
}
|
||||
largest = left;
|
||||
if (left + 1 < nmemb && compar(elem(base, size, left), elem(base, size, left + 1)) < 0)
|
||||
{
|
||||
largest = left + 1;
|
||||
}
|
||||
if (compar(elem(base, size, root), elem(base, size, largest)) >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
swap_bytes(elem(base, size, root), elem(base, size, largest), size);
|
||||
root = largest;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
heapsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = nmemb / 2; i > 0; i--)
|
||||
{
|
||||
sift_down(base, nmemb, size, i - 1, compar);
|
||||
}
|
||||
for (i = nmemb; i > 1; i--)
|
||||
{
|
||||
swap_bytes(base, elem(base, size, i - 1), size);
|
||||
sift_down(base, i - 1, size, 0, compar);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort elements lo..hi; recurses on the smaller partition half and
|
||||
* iterates the larger. depth_limit counts remaining quicksort levels. */
|
||||
static void
|
||||
introsort_range(void *base, size_t size, size_t lo, size_t hi, // NOLINT(misc-no-recursion)
|
||||
int (*compar)(const void *, const void *), int depth_limit)
|
||||
{
|
||||
while (hi - lo >= 16)
|
||||
{
|
||||
size_t p;
|
||||
|
||||
if (depth_limit == 0)
|
||||
{
|
||||
heapsort(elem(base, size, lo), hi - lo + 1, size, compar);
|
||||
return;
|
||||
}
|
||||
depth_limit--;
|
||||
|
||||
{
|
||||
size_t mid = lo + ((hi - lo) / 2);
|
||||
|
||||
/*
|
||||
* Median of three by value: sort the samples so the median
|
||||
* sits at mid, then park it just inside the max sample at
|
||||
* hi. The min sample at lo and the max at hi are the outer
|
||||
* sentinels of the partition range [lo + 1, hi - 2]: the
|
||||
* i-scan is bounded at hi - 2 and the j-scan at lo, so the
|
||||
* parked pivot pointer itself is never compared against.
|
||||
*/
|
||||
sort_samples(base, size, lo, mid, hi, compar);
|
||||
swap_bytes(elem(base, size, mid), elem(base, size, hi - 1), size);
|
||||
}
|
||||
|
||||
{
|
||||
void *pivot_value = elem(base, size, hi - 1);
|
||||
size_t i = lo + 1;
|
||||
size_t j = hi - 2;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while (i <= hi - 2 && compar(elem(base, size, i), pivot_value) < 0)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
while (j > lo && compar(elem(base, size, j), pivot_value) > 0)
|
||||
{
|
||||
j--;
|
||||
}
|
||||
if (i >= j)
|
||||
{
|
||||
break;
|
||||
}
|
||||
swap_bytes(elem(base, size, i), elem(base, size, j), size);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
p = i;
|
||||
if (p != hi - 1)
|
||||
{
|
||||
swap_bytes(elem(base, size, p), elem(base, size, hi - 1), size);
|
||||
}
|
||||
}
|
||||
|
||||
if (p - lo < hi - p)
|
||||
{
|
||||
if (p > lo)
|
||||
{
|
||||
introsort_range(base, size, lo, p - 1, compar, depth_limit);
|
||||
}
|
||||
lo = p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p < hi)
|
||||
{
|
||||
introsort_range(base, size, p + 1, hi, compar, depth_limit);
|
||||
}
|
||||
hi = p - 1;
|
||||
}
|
||||
}
|
||||
insertion_sort(elem(base, size, lo), hi - lo + 1, size, compar);
|
||||
}
|
||||
|
||||
void
|
||||
qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
|
||||
{
|
||||
int depth_limit = 0;
|
||||
size_t n = nmemb;
|
||||
|
||||
if (nmemb < 2 || size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (n > 1)
|
||||
{
|
||||
depth_limit++;
|
||||
n >>= 1;
|
||||
}
|
||||
depth_limit *= 2;
|
||||
introsort_range(base, size, 0, nmemb - 1, compar, depth_limit);
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
void *
|
||||
bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *))
|
||||
{
|
||||
size_t lo = 0;
|
||||
size_t hi = nmemb;
|
||||
|
||||
while (lo < hi)
|
||||
{
|
||||
size_t mid = lo + ((hi - lo) / 2);
|
||||
const void *p = (const unsigned char *)base + (mid * size);
|
||||
int c = compar(key, p);
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
hi = mid;
|
||||
}
|
||||
else if (c > 0)
|
||||
{
|
||||
lo = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (void *)p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
@@ -0,0 +1,214 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Pseudo-random number generation (todo 12). C23 7.22.2 core rand/srand
|
||||
* at level 1; the reentrant rand_r and the XSI random and drand48
|
||||
* families at level 2.
|
||||
*
|
||||
* All generators are deterministic linear congruential generators with
|
||||
* fixed constants (any constants are valid: the standard pins only
|
||||
* determinism per seed and the output range). State lives in file-static
|
||||
* variables in plain snake_case — no implementation-reserved identifiers.
|
||||
*/
|
||||
|
||||
/* rand/srand: a 64-bit LCG whose top 31 bits form the result, so the
|
||||
* output spans exactly [0, 2^31 - 1] = [0, RAND_MAX]. */
|
||||
static unsigned long long rand_state = 1;
|
||||
|
||||
int
|
||||
rand(void)
|
||||
{
|
||||
rand_state = rand_state * 6364136223846793005ULL + 1ULL;
|
||||
return (int)((rand_state >> 33) & 0x7fffffff);
|
||||
}
|
||||
|
||||
void
|
||||
srand(unsigned int seed)
|
||||
{
|
||||
rand_state = (unsigned long long)seed;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* rand_r: the reentrant form. The caller's *seedp is both input and
|
||||
* output state, entirely independent of rand()'s rand_state. The low
|
||||
* multiply is deliberately 32-bit (wrapping unsigned arithmetic), which
|
||||
* keeps the sequence portable across 64-bit and 32-bit targets.
|
||||
*/
|
||||
int
|
||||
rand_r(unsigned int *seedp)
|
||||
{
|
||||
unsigned int next = *seedp;
|
||||
|
||||
next = next * 1103515245U + 12345U;
|
||||
*seedp = next;
|
||||
return (int)(next >> 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* random/srandom/initstate/setstate: a 31-bit generator whose state
|
||||
* lives in a caller-provided long buffer. The first long of the buffer
|
||||
* is the generator value; initstate checks the size, installs the
|
||||
* buffer, seeds it, and returns the previous buffer (NULL when the
|
||||
* buffer is too small); setstate installs and returns the previous
|
||||
* buffer (NULL for a NULL argument); srandom reseeds whatever buffer is
|
||||
* installed. The default buffer is the file-static array below.
|
||||
*/
|
||||
static long default_random_state;
|
||||
|
||||
static long *random_state_pointer = &default_random_state;
|
||||
|
||||
long
|
||||
random(void)
|
||||
{
|
||||
long x = *random_state_pointer;
|
||||
|
||||
x = x * 6364136223846793005L + 1L;
|
||||
*random_state_pointer = x;
|
||||
return (x >> 33) & 0x7fffffffL;
|
||||
}
|
||||
|
||||
void
|
||||
srandom(unsigned int seed)
|
||||
{
|
||||
*random_state_pointer = (long)seed;
|
||||
}
|
||||
|
||||
char *
|
||||
initstate(unsigned int seed, char *state, size_t size)
|
||||
{
|
||||
char *old = (char *)random_state_pointer;
|
||||
|
||||
if (size < sizeof(long))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
random_state_pointer = (long *)state;
|
||||
srandom(seed);
|
||||
return old;
|
||||
}
|
||||
|
||||
char *
|
||||
setstate(char *state)
|
||||
{
|
||||
char *old = (char *)random_state_pointer;
|
||||
|
||||
if (state == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
random_state_pointer = (long *)state;
|
||||
return old;
|
||||
}
|
||||
|
||||
/*
|
||||
* drand48 family: the standard 48-bit LCG
|
||||
*
|
||||
* x' = (x * A + C) mod 2^48, A = 0x5DEECE66D, C = 0xB
|
||||
*
|
||||
* carried in three unsigned shorts, least-significant short first
|
||||
* (xsubi[0] is the low 16 bits), so the layout matches the XSI
|
||||
* declaration order. A and C are file-static: lcong48 mutates them, and
|
||||
* srand48/seed48 reset them to the defaults. The default state
|
||||
* X = 0x1234ABCD330E.
|
||||
*/
|
||||
static unsigned short drand48_state[3] = {0x330E, 0xABCD, 0x1234};
|
||||
static unsigned long long drand48_mult = 0x5DEECE66DULL;
|
||||
static unsigned long long drand48_add = 0xBULL;
|
||||
|
||||
static unsigned long long
|
||||
drand48_step(unsigned short xsubi[3])
|
||||
{
|
||||
unsigned long long x = (unsigned long long)xsubi[0] | ((unsigned long long)xsubi[1] << 16) |
|
||||
((unsigned long long)xsubi[2] << 32);
|
||||
|
||||
x = (x * drand48_mult + drand48_add) & 0xFFFFFFFFFFFFULL;
|
||||
xsubi[0] = (unsigned short)x;
|
||||
xsubi[1] = (unsigned short)(x >> 16);
|
||||
xsubi[2] = (unsigned short)(x >> 32);
|
||||
return x;
|
||||
}
|
||||
|
||||
double
|
||||
drand48(void)
|
||||
{
|
||||
return (double)drand48_step(drand48_state) / 281474976710656.0;
|
||||
}
|
||||
|
||||
double
|
||||
erand48(unsigned short xsubi[3])
|
||||
{
|
||||
return (double)drand48_step(xsubi) / 281474976710656.0;
|
||||
}
|
||||
|
||||
long
|
||||
lrand48(void)
|
||||
{
|
||||
return (long)(drand48_step(drand48_state) >> 17);
|
||||
}
|
||||
|
||||
long
|
||||
nrand48(unsigned short xsubi[3])
|
||||
{
|
||||
return (long)(drand48_step(xsubi) >> 17);
|
||||
}
|
||||
|
||||
long
|
||||
mrand48(void)
|
||||
{
|
||||
return (long)(int)(drand48_step(drand48_state) >> 16);
|
||||
}
|
||||
|
||||
long
|
||||
jrand48(unsigned short xsubi[3])
|
||||
{
|
||||
return (long)(int)(drand48_step(xsubi) >> 16);
|
||||
}
|
||||
|
||||
void
|
||||
srand48(long seedval)
|
||||
{
|
||||
unsigned long long x = (((unsigned long long)seedval << 16) | 0x330EULL) & 0xFFFFFFFFFFFFULL;
|
||||
|
||||
drand48_state[0] = (unsigned short)x;
|
||||
drand48_state[1] = (unsigned short)(x >> 16);
|
||||
drand48_state[2] = (unsigned short)(x >> 32);
|
||||
drand48_mult = 0x5DEECE66DULL;
|
||||
drand48_add = 0xBULL;
|
||||
}
|
||||
|
||||
unsigned short *
|
||||
seed48(unsigned short seed16v[3])
|
||||
{
|
||||
static unsigned short previous_state[3];
|
||||
|
||||
previous_state[0] = drand48_state[0];
|
||||
previous_state[1] = drand48_state[1];
|
||||
previous_state[2] = drand48_state[2];
|
||||
drand48_state[0] = seed16v[0];
|
||||
drand48_state[1] = seed16v[1];
|
||||
drand48_state[2] = seed16v[2];
|
||||
drand48_mult = 0x5DEECE66DULL;
|
||||
drand48_add = 0xBULL;
|
||||
return previous_state;
|
||||
}
|
||||
|
||||
void
|
||||
lcong48(unsigned short param[7])
|
||||
{
|
||||
drand48_state[0] = param[0];
|
||||
drand48_state[1] = param[1];
|
||||
drand48_state[2] = param[2];
|
||||
drand48_mult = (unsigned long long)param[3] | ((unsigned long long)param[4] << 16) |
|
||||
((unsigned long long)param[5] << 32);
|
||||
drand48_add = (unsigned long long)param[6];
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
Reference in New Issue
Block a user