diff --git a/include/stdlib.h b/include/stdlib.h index a178fb6..44ab589 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -201,6 +201,172 @@ strtof(const char *restrict nptr, char **restrict endptr); long double 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 } #endif diff --git a/src/stdlib/div.c b/src/stdlib/div.c new file mode 100644 index 0000000..2920927 --- /dev/null +++ b/src/stdlib/div.c @@ -0,0 +1,84 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include + +/* + * 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; +} diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c new file mode 100644 index 0000000..aadf1cb --- /dev/null +++ b/src/stdlib/qsort.c @@ -0,0 +1,281 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +/* + * 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) diff --git a/src/stdlib/rand.c b/src/stdlib/rand.c new file mode 100644 index 0000000..b37811a --- /dev/null +++ b/src/stdlib/rand.c @@ -0,0 +1,214 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +/* + * 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) */ diff --git a/tests/test_qsort.c b/tests/test_qsort.c new file mode 100644 index 0000000..7984e63 --- /dev/null +++ b/tests/test_qsort.c @@ -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 + * 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 + +#include + +#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; +}