feat(stdlib): rand/div/qsort/bsearch
This commit is contained in:
@@ -201,6 +201,172 @@ strtof(const char *restrict nptr, char **restrict endptr);
|
|||||||
long double
|
long double
|
||||||
strtold(const char *restrict nptr, char **restrict endptr);
|
strtold(const char *restrict nptr, char **restrict endptr);
|
||||||
|
|
||||||
|
/* pseudo-random numbers, search, and integer arithmetic (todo 12) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C23 7.22.2/7.22.5/7.22.6: rand/srand, qsort/bsearch, the abs family, and
|
||||||
|
* the div family are ISO C core and present in every profile. The abs and
|
||||||
|
* div functions compute a pure function of their arguments, so they are
|
||||||
|
* declared const (the compiler folds and eliminates the calls in static
|
||||||
|
* links; GCC's own builtin declarations of abs/labs/llabs are const, and
|
||||||
|
* the attribute here matches them). rand and srand carry state, and
|
||||||
|
* qsort/bsearch call a caller-supplied comparator, so none of those four
|
||||||
|
* carries an intent attribute.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The largest value rand() returns: 2^31 - 1, the largest int.
|
||||||
|
*/
|
||||||
|
#define RAND_MAX 2147483647
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quotient/remainder pair from div/ldiv/lldiv: quot is the algebraic
|
||||||
|
* quotient truncated toward zero, rem the remainder of the same sign as
|
||||||
|
* the dividend, and quot*denom + rem == num.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int quot;
|
||||||
|
int rem;
|
||||||
|
} div_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
long quot;
|
||||||
|
long rem;
|
||||||
|
} ldiv_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
long long quot;
|
||||||
|
long long rem;
|
||||||
|
} lldiv_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Absolute value of n. The most-negative value returns itself: the
|
||||||
|
* negation happens in the unsigned type, which wraps, so the result is
|
||||||
|
* never undefined behavior and errno is never set.
|
||||||
|
*/
|
||||||
|
__attribute__((const)) int
|
||||||
|
abs(int n);
|
||||||
|
|
||||||
|
__attribute__((const)) long
|
||||||
|
labs(long n);
|
||||||
|
|
||||||
|
__attribute__((const)) long long
|
||||||
|
llabs(long long n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quotient and remainder of num/denom, truncated toward zero.
|
||||||
|
*/
|
||||||
|
__attribute__((const)) div_t
|
||||||
|
div(int num, int denom);
|
||||||
|
|
||||||
|
__attribute__((const)) ldiv_t
|
||||||
|
ldiv(long num, long denom);
|
||||||
|
|
||||||
|
__attribute__((const)) lldiv_t
|
||||||
|
lldiv(long long num, long long denom);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pseudo-random integer in [0, RAND_MAX], deterministic for a given
|
||||||
|
* srand seed.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rand(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Seed the rand() sequence with seed.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
srand(unsigned int seed);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sort the array of nmemb elements of size bytes at base into ascending
|
||||||
|
* order according to compar (C23 7.22.5.2). compar receives two pointers
|
||||||
|
* to distinct elements and returns negative/zero/positive. The sort is
|
||||||
|
* not stable.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Binary-search the array of nmemb sorted elements of size bytes at base
|
||||||
|
* for *key, calling compar(key, element) (C23 7.22.5.1). Returns a
|
||||||
|
* pointer to the matching element, or NULL when there is none.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||||
|
int (*compar)(const void *, const void *));
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
/* Level 2 (muslmimic): XSI and obsolescent extensions. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reentrant rand: the state lives in the caller's *seedp, which is
|
||||||
|
* updated on every call, so the sequence is independent of rand()'s own
|
||||||
|
* global state.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rand_r(unsigned int *seedp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* random/srandom family (XSI): a 31-bit pseudo-random sequence in
|
||||||
|
* [0, 2^31). initstate installs state (size bytes, at least
|
||||||
|
* sizeof(long)) as the current state buffer, seeds it, and returns the
|
||||||
|
* previous buffer; setstate installs the buffer and returns the previous
|
||||||
|
* one. srandom reseeds the current buffer.
|
||||||
|
*/
|
||||||
|
long
|
||||||
|
random(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
srandom(unsigned int seed);
|
||||||
|
|
||||||
|
char *
|
||||||
|
initstate(unsigned int seed, char *state, size_t size);
|
||||||
|
|
||||||
|
char *
|
||||||
|
setstate(char *state);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drand48 family (XSI): a 48-bit linear congruential sequence carried in
|
||||||
|
* three unsigned shorts, least-significant first. drand48/erand48 return
|
||||||
|
* the current value divided by 2^48 as a double in [0, 1); lrand48/
|
||||||
|
* nrand48 return the high 31 bits; mrand48/jrand48 the high 32 bits
|
||||||
|
* sign-extended. The erand48/nrand48/jrand48 forms step the caller's
|
||||||
|
* xsubi in place; srand48/seed48/lcong48 manage the shared state and the
|
||||||
|
* multiplier/addend.
|
||||||
|
*/
|
||||||
|
double
|
||||||
|
drand48(void);
|
||||||
|
|
||||||
|
double
|
||||||
|
erand48(unsigned short xsubi[3]);
|
||||||
|
|
||||||
|
long
|
||||||
|
lrand48(void);
|
||||||
|
|
||||||
|
long
|
||||||
|
nrand48(unsigned short xsubi[3]);
|
||||||
|
|
||||||
|
long
|
||||||
|
mrand48(void);
|
||||||
|
|
||||||
|
long
|
||||||
|
jrand48(unsigned short xsubi[3]);
|
||||||
|
|
||||||
|
void
|
||||||
|
srand48(long seedval);
|
||||||
|
|
||||||
|
unsigned short *
|
||||||
|
seed48(unsigned short seed16v[3]);
|
||||||
|
|
||||||
|
void
|
||||||
|
lcong48(unsigned short param[7]);
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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) */
|
||||||
@@ -0,0 +1,526 @@
|
|||||||
|
/*
|
||||||
|
* vlibc — rand/srand, abs/div, qsort/bsearch test (todo 12).
|
||||||
|
*
|
||||||
|
* Exercises the stdlib todo-12 additions end to end:
|
||||||
|
*
|
||||||
|
* 1. qsort on 0 and 1 element (the comparator is never called), 2048
|
||||||
|
* pseudo-random ints, duplicate-heavy, reverse-sorted, and
|
||||||
|
* all-equal arrays — each verified ascending with the element-sum
|
||||||
|
* checksum preserved.
|
||||||
|
* 2. bsearch finds a present key and returns NULL for an absent one.
|
||||||
|
* 3. rand is deterministic per seed and stays inside [0, RAND_MAX];
|
||||||
|
* div(7,3) == {2,1}, div(-7,3) == {-2,-1}, ldiv/lldiv spot checks;
|
||||||
|
* abs/labs/llabs basics plus the INT_MIN/LONG_MIN/LLONG_MIN wrap
|
||||||
|
* through volatile locals so the compiler cannot fold the negation.
|
||||||
|
* 4. Level 2: rand_r determinism and independence from rand()'s
|
||||||
|
* global state, srand48 reseed determinism, drand48 samples in
|
||||||
|
* [0,1), lrand48 in [0,2^31), erand48 stepping the caller's xsubi.
|
||||||
|
*
|
||||||
|
* The instrumented comparator counts calls and records any call where
|
||||||
|
* both arguments point at the same element; every scenario asserts the
|
||||||
|
* self-compare flag stays clear.
|
||||||
|
*
|
||||||
|
* The -f mode stress-sorts three static-BSS arrays of 100000 ints
|
||||||
|
* (reverse-sorted, already-sorted, all-equal) with the instrumented
|
||||||
|
* comparator, verifies ascending order and an O(n log n) comparison
|
||||||
|
* count (< 5.2M, far below the ~5e9 an O(n^2) sort would need), and
|
||||||
|
* exits plainly — nothing here writes errno, so a normal return is
|
||||||
|
* safe.
|
||||||
|
*
|
||||||
|
* All diagnostics go through raw SYS_write (no stdio): under -Iinclude
|
||||||
|
* the vlibc public headers shadow GCC's internal ones, so a host
|
||||||
|
* <stdio.h> would not compile. The stdlib.h below is vlibc's own.
|
||||||
|
*
|
||||||
|
* Not part of the library proper; compiled manually for this todo (the
|
||||||
|
* tests/ + make check wiring is owned by a later todo).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "../include/stdlib.h"
|
||||||
|
|
||||||
|
#include "../src/internal/syscall.h"
|
||||||
|
|
||||||
|
static int failures;
|
||||||
|
|
||||||
|
/* Write a NUL-terminated string to fd via the raw syscall layer. */
|
||||||
|
static void
|
||||||
|
say(int fd, const char *s)
|
||||||
|
{
|
||||||
|
long n = 0;
|
||||||
|
|
||||||
|
while (s[n] != '\0')
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
__syscall3(SYS_write, fd, (long)s, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write v in decimal to fd. */
|
||||||
|
static void
|
||||||
|
say_dec(int fd, unsigned long v) // NOLINT(bugprone-easily-swappable-parameters)
|
||||||
|
{
|
||||||
|
char buf[24];
|
||||||
|
int i = (int)sizeof(buf);
|
||||||
|
|
||||||
|
buf[--i] = '\0';
|
||||||
|
do
|
||||||
|
{
|
||||||
|
buf[--i] = (char)('0' + (v % 10));
|
||||||
|
v /= 10;
|
||||||
|
} while (v != 0);
|
||||||
|
__syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check(int cond, const char *what)
|
||||||
|
{
|
||||||
|
if (cond)
|
||||||
|
{
|
||||||
|
say(1, "PASS: ");
|
||||||
|
say(1, what);
|
||||||
|
say(1, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
say(2, "FAIL: ");
|
||||||
|
say(2, what);
|
||||||
|
say(2, "\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* xorshift32; deterministic, allocation-independent. */
|
||||||
|
static unsigned
|
||||||
|
rng_next(unsigned *state)
|
||||||
|
{
|
||||||
|
unsigned x = *state;
|
||||||
|
|
||||||
|
x ^= x << 13;
|
||||||
|
x ^= x >> 17;
|
||||||
|
x ^= x << 5;
|
||||||
|
*state = x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Instrumented int comparator: counts calls and records self-compares. */
|
||||||
|
static long compar_count;
|
||||||
|
static int self_compare_seen;
|
||||||
|
|
||||||
|
static int
|
||||||
|
int_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int *pa = a;
|
||||||
|
const int *pb = b;
|
||||||
|
|
||||||
|
compar_count++;
|
||||||
|
if (a == b)
|
||||||
|
{
|
||||||
|
self_compare_seen = 1;
|
||||||
|
}
|
||||||
|
if (*pa < *pb)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (*pa > *pb)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sort arr (n ints) and verify ascending order and a preserved sum. */
|
||||||
|
static int
|
||||||
|
sort_and_verify(int *arr, size_t n, const char *what)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
long long before = 0;
|
||||||
|
long long after = 0;
|
||||||
|
int asc = 1;
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
before += arr[i];
|
||||||
|
}
|
||||||
|
compar_count = 0;
|
||||||
|
qsort(arr, n, sizeof(int), int_cmp);
|
||||||
|
for (i = 1; i < n; i++)
|
||||||
|
{
|
||||||
|
if (arr[i - 1] > arr[i])
|
||||||
|
{
|
||||||
|
asc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
after += arr[i];
|
||||||
|
}
|
||||||
|
if (!asc)
|
||||||
|
{
|
||||||
|
say(2, "FAIL: ");
|
||||||
|
say(2, what);
|
||||||
|
say(2, " - not ascending\n");
|
||||||
|
failures++;
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
if (before != after)
|
||||||
|
{
|
||||||
|
say(2, "FAIL: ");
|
||||||
|
say(2, what);
|
||||||
|
say(2, " - element sum changed\n");
|
||||||
|
failures++;
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
say(1, "PASS: ");
|
||||||
|
say(1, what);
|
||||||
|
say(1, "\n");
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
qsort_scenarios(void)
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
N = 2048
|
||||||
|
};
|
||||||
|
int arr[N];
|
||||||
|
unsigned i;
|
||||||
|
unsigned rng = 0x2545F491U;
|
||||||
|
int one = 7;
|
||||||
|
|
||||||
|
compar_count = 0;
|
||||||
|
qsort(arr, 0, sizeof(int), int_cmp);
|
||||||
|
qsort(&one, 1, sizeof(int), int_cmp);
|
||||||
|
check(compar_count == 0, "qsort(0) and qsort(1) never call the comparator");
|
||||||
|
|
||||||
|
for (i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
arr[i] = (int)rng_next(&rng);
|
||||||
|
}
|
||||||
|
sort_and_verify(arr, N, "2048 pseudo-random ints sort");
|
||||||
|
|
||||||
|
for (i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
arr[i] = (int)(i % 7);
|
||||||
|
}
|
||||||
|
sort_and_verify(arr, N, "2048 duplicate-heavy ints sort");
|
||||||
|
|
||||||
|
for (i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
arr[i] = (int)(N - 1 - i);
|
||||||
|
}
|
||||||
|
sort_and_verify(arr, N, "2048 reverse-sorted ints sort");
|
||||||
|
|
||||||
|
for (i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
arr[i] = 5;
|
||||||
|
}
|
||||||
|
sort_and_verify(arr, N, "2048 all-equal ints sort");
|
||||||
|
|
||||||
|
check(self_compare_seen == 0, "comparator never saw the same element twice");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bsearch_tests(void)
|
||||||
|
{
|
||||||
|
int arr[16];
|
||||||
|
unsigned i;
|
||||||
|
int key;
|
||||||
|
int *p;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
arr[i] = (int)(i * 2);
|
||||||
|
}
|
||||||
|
key = 14;
|
||||||
|
p = bsearch(&key, arr, 16, sizeof(int), int_cmp);
|
||||||
|
check(p != NULL && *p == key, "bsearch finds a present key");
|
||||||
|
key = 13;
|
||||||
|
p = bsearch(&key, arr, 16, sizeof(int), int_cmp);
|
||||||
|
check(p == NULL, "bsearch returns NULL for an absent key");
|
||||||
|
check(self_compare_seen == 0, "bsearch never self-compared");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rand_tests(void)
|
||||||
|
{
|
||||||
|
int a[8];
|
||||||
|
int b[8];
|
||||||
|
unsigned i;
|
||||||
|
int same = 1;
|
||||||
|
int in_range = 1;
|
||||||
|
|
||||||
|
srand(1);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
a[i] = rand();
|
||||||
|
}
|
||||||
|
srand(1);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
b[i] = rand();
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (a[i] != b[i])
|
||||||
|
{
|
||||||
|
same = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check(same, "srand(1) twice gives identical rand sequences");
|
||||||
|
srand(12345);
|
||||||
|
for (i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
int v = rand();
|
||||||
|
|
||||||
|
if (v < 0 || v > RAND_MAX)
|
||||||
|
{
|
||||||
|
in_range = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check(in_range, "rand values stay within [0, RAND_MAX]");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
div_tests(void)
|
||||||
|
{
|
||||||
|
div_t d = div(7, 3);
|
||||||
|
ldiv_t ld = ldiv(17L, 5L);
|
||||||
|
ldiv_t ldn = ldiv(-17L, 5L);
|
||||||
|
lldiv_t lld = lldiv(1000000000000000LL, 3LL);
|
||||||
|
|
||||||
|
check(d.quot == 2 && d.rem == 1, "div(7,3) == {2,1}");
|
||||||
|
d = div(-7, 3);
|
||||||
|
check(d.quot == -2 && d.rem == -1, "div(-7,3) == {-2,-1}");
|
||||||
|
check(ld.quot == 3L && ld.rem == 2L, "ldiv(17,5) == {3,2}");
|
||||||
|
check(ldn.quot == -3L && ldn.rem == -2L, "ldiv(-17,5) == {-3,-2}");
|
||||||
|
check(lld.quot == 333333333333333LL && lld.rem == 1LL, "lldiv(10^15,3) == {333333333333333,1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* noipa proxies for the abs family: GCC knows abs/labs/llabs as const
|
||||||
|
* builtins whose result lies in [0, type_MAX], so even with a volatile
|
||||||
|
* argument its value-range analysis proves `abs(vi) == INT_MIN` false
|
||||||
|
* and folds the check before the call. noipa blocks interprocedural
|
||||||
|
* propagation through the proxy (the repo's established pattern, see
|
||||||
|
* the SIZE_MAX-10 handling above); the volatile locals then force the
|
||||||
|
* wrap values to exist at runtime.
|
||||||
|
*/
|
||||||
|
static __attribute__((noipa)) int
|
||||||
|
abs_proxy(int n)
|
||||||
|
{
|
||||||
|
return abs(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __attribute__((noipa)) long
|
||||||
|
labs_proxy(long n)
|
||||||
|
{
|
||||||
|
return labs(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __attribute__((noipa)) long long
|
||||||
|
llabs_proxy(long long n)
|
||||||
|
{
|
||||||
|
return llabs(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
abs_tests(void)
|
||||||
|
{
|
||||||
|
volatile int vi = INT_MIN;
|
||||||
|
volatile long vl = LONG_MIN;
|
||||||
|
volatile long long vll = LLONG_MIN;
|
||||||
|
|
||||||
|
check(abs(-7) == 7 && abs(7) == 7, "abs basics");
|
||||||
|
check(labs(-7L) == 7L, "labs basics");
|
||||||
|
check(llabs(-7LL) == 7LL, "llabs basics");
|
||||||
|
check(abs_proxy(vi) == INT_MIN, "abs(INT_MIN) wraps to INT_MIN");
|
||||||
|
check(labs_proxy(vl) == LONG_MIN, "labs(LONG_MIN) wraps to LONG_MIN");
|
||||||
|
check(llabs_proxy(vll) == LLONG_MIN, "llabs(LLONG_MIN) wraps to LLONG_MIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
static void
|
||||||
|
rand_r_tests(void)
|
||||||
|
{
|
||||||
|
unsigned s1 = 123;
|
||||||
|
unsigned s2 = 123;
|
||||||
|
unsigned s3 = 99;
|
||||||
|
int r1 = rand_r(&s1);
|
||||||
|
int r2 = rand_r(&s2);
|
||||||
|
int before;
|
||||||
|
int after;
|
||||||
|
|
||||||
|
check(r1 == r2 && s1 == s2, "rand_r: equal seeds give equal results and state");
|
||||||
|
srand(7);
|
||||||
|
before = rand();
|
||||||
|
rand_r(&s3);
|
||||||
|
rand();
|
||||||
|
srand(7);
|
||||||
|
after = rand();
|
||||||
|
check(before == after, "rand_r does not disturb rand's global state");
|
||||||
|
s1 = 5;
|
||||||
|
s2 = 5;
|
||||||
|
rand();
|
||||||
|
rand();
|
||||||
|
check(rand_r(&s1) == rand_r(&s2), "rand_r independent of interleaved rand() calls");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
drand48_tests(void)
|
||||||
|
{
|
||||||
|
double d1;
|
||||||
|
double d2;
|
||||||
|
unsigned i;
|
||||||
|
int ok = 1;
|
||||||
|
unsigned short xs[3] = {0x330E, 0xABCD, 0x1234};
|
||||||
|
double e1 = erand48(xs);
|
||||||
|
|
||||||
|
srand48(42);
|
||||||
|
d1 = drand48();
|
||||||
|
srand48(42);
|
||||||
|
d2 = drand48();
|
||||||
|
check(d1 == d2, "srand48 reseed gives deterministic drand48");
|
||||||
|
srand48(1);
|
||||||
|
for (i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
double v = drand48();
|
||||||
|
|
||||||
|
if (!(v >= 0.0 && v < 1.0))
|
||||||
|
{
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check(ok, "drand48: 1000 samples in [0,1)");
|
||||||
|
srand48(2);
|
||||||
|
ok = 1;
|
||||||
|
for (i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
long v = lrand48();
|
||||||
|
|
||||||
|
if (v < 0 || v >= 2147483648L)
|
||||||
|
{
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check(ok, "lrand48: 1000 samples in [0, 2^31)");
|
||||||
|
check(!(xs[0] == 0x330E && xs[1] == 0xABCD && xs[2] == 0x1234),
|
||||||
|
"erand48 steps the caller's xsubi in place");
|
||||||
|
srand48(0x1234ABCDL);
|
||||||
|
check(e1 == drand48(), "erand48(default xsubi) matches srand48(0x1234ABCD) drand48");
|
||||||
|
}
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
|
|
||||||
|
/* -f mode: three 100k-element stress sorts from static BSS (no malloc). */
|
||||||
|
#define BIG_N 100000
|
||||||
|
|
||||||
|
static int big_array[BIG_N];
|
||||||
|
|
||||||
|
static int
|
||||||
|
stress_case(const char *label)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
int asc = 1;
|
||||||
|
|
||||||
|
compar_count = 0;
|
||||||
|
qsort(big_array, BIG_N, sizeof(int), int_cmp);
|
||||||
|
for (i = 1; i < BIG_N; i++)
|
||||||
|
{
|
||||||
|
if (big_array[i - 1] > big_array[i])
|
||||||
|
{
|
||||||
|
asc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
say(1, label);
|
||||||
|
say(1, " comparisons: ");
|
||||||
|
say_dec(1, (unsigned long)compar_count);
|
||||||
|
say(1, "\n");
|
||||||
|
if (!asc)
|
||||||
|
{
|
||||||
|
say(2, "FAIL: ");
|
||||||
|
say(2, label);
|
||||||
|
say(2, " - not ascending\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (compar_count >= 5200000L)
|
||||||
|
{
|
||||||
|
say(2, "FAIL: ");
|
||||||
|
say(2, label);
|
||||||
|
say(2, " - comparisons >= 5200000 (not O(n log n))\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (self_compare_seen)
|
||||||
|
{
|
||||||
|
say(2, "FAIL: ");
|
||||||
|
say(2, label);
|
||||||
|
say(2, " - self-compare observed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
say(1, "PASS: ");
|
||||||
|
say(1, label);
|
||||||
|
say(1, "\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stress_mode(void)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
self_compare_seen = 0;
|
||||||
|
for (i = 0; i < BIG_N; i++)
|
||||||
|
{
|
||||||
|
big_array[i] = (int)(BIG_N - 1 - i);
|
||||||
|
}
|
||||||
|
rc |= stress_case("100k reverse-sorted");
|
||||||
|
|
||||||
|
for (i = 0; i < BIG_N; i++)
|
||||||
|
{
|
||||||
|
big_array[i] = (int)i;
|
||||||
|
}
|
||||||
|
rc |= stress_case("100k already-sorted");
|
||||||
|
|
||||||
|
for (i = 0; i < BIG_N; i++)
|
||||||
|
{
|
||||||
|
big_array[i] = 42;
|
||||||
|
}
|
||||||
|
rc |= stress_case("100k all-equal");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
|
||||||
|
{
|
||||||
|
return stress_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort_scenarios();
|
||||||
|
bsearch_tests();
|
||||||
|
rand_tests();
|
||||||
|
div_tests();
|
||||||
|
abs_tests();
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
rand_r_tests();
|
||||||
|
drand48_tests();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (failures > 0)
|
||||||
|
{
|
||||||
|
say(2, "FAILED (");
|
||||||
|
say_dec(2, (unsigned long)failures);
|
||||||
|
say(2, " check(s))\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
say(1, "all rand/div/qsort/bsearch tests passed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user