diff --git a/libc/rand/rand.h b/libc/rand/rand.h index 96293cd3..573658a5 100644 --- a/libc/rand/rand.h +++ b/libc/rand/rand.h @@ -6,7 +6,7 @@ COSMOPOLITAN_C_START_ │ cosmopolitan § random ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -#define RAND_MAX __INT_MAX__ /* only applies to rand() */ +#define RAND_MAX __INT_MAX__ /* only applies to rand() */ void srand(uint64_t) nothrow nocallback; /* seeds rand() only */ int rand(void) nothrow nocallback; /* ≥0 unseeded lcg prng */ uint32_t rand32(void) nothrow nocallback; /* random as possible rng */ @@ -25,6 +25,9 @@ int64_t winrandish(void); uint64_t rdrand(void); uint64_t rdseed(void); float randf(void); +char *initstate(unsigned, char *, size_t); +char *setstate(char *); +long random(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/rand/random.c b/libc/rand/random.c new file mode 100644 index 00000000..bfae5347 --- /dev/null +++ b/libc/rand/random.c @@ -0,0 +1,128 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ 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/rand/rand.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); + +/* + * this code uses the same lagged fibonacci generator as the + * original bsd random implementation except for the seeding + * which was broken in the original + */ + +static uint32_t init[] = { + 0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646, 0xc7033129, 0x30705b04, + 0x20fd5db4, 0x9a8b7f78, 0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff, + 0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc, 0x8ae16fd9, 0x742d2f7a, + 0x0d1f0796, 0x76035e09, 0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74, + 0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115, 0x0cab8628, 0xf043bfa4, + 0x398150e9, 0x37521657, +}; + +static int n = 31; +static int i = 3; +static int j = 0; +static uint32_t *x = init + 1; + +static uint32_t lcg31(uint32_t x) { + return (1103515245 * x + 12345) & 0x7fffffff; +} + +static uint64_t lcg64(uint64_t x) { + return 6364136223846793005ull * x + 1; +} + +static void *savestate(void) { + x[-1] = (n << 16) | (i << 8) | j; + return x - 1; +} + +static void loadstate(uint32_t *state) { + x = state + 1; + n = x[-1] >> 16; + i = (x[-1] >> 8) & 0xff; + j = x[-1] & 0xff; +} + +void srandom(unsigned seed) { + int k; + uint64_t s = seed; + if (!n) { + x[0] = s; + return; + } + i = n == 31 || n == 7 ? 3 : 1; + j = 0; + for (k = 0; k < n; k++) { + s = lcg64(s); + x[k] = s >> 32; + } + /* make sure x contains at least one odd number */ + x[0] |= 1; +} + +char *initstate(unsigned seed, char *state, size_t size) { + void *old; + if (size < 8) return 0; + old = savestate(); + if (size < 32) { + n = 0; + } else if (size < 64) { + n = 7; + } else if (size < 128) { + n = 15; + } else if (size < 256) { + n = 31; + } else { + n = 63; + } + x = (uint32_t *)state + 1; + srandom(seed); + savestate(); + return old; +} + +char *setstate(char *state) { + void *old; + old = savestate(); + loadstate((uint32_t *)state); + return old; +} + +long random(void) { + long k; + if (!n) return (x[0] = lcg31(x[0])); + x[i] += x[j]; + k = x[i] >> 1; + if (++i == n) i = 0; + if (++j == n) j = 0; + return k; +}