/* * implementation of LCG RNG, using constants found in Wikipedia article * * https://en.wikipedia.org/wiki/Linear_congruential_generator * * (the values it says are used in Java's "Random" and glibc's drand48) * * and leapfrogging as described in homework writeup: * * a' = (A**p) mod M * b' = (B*(A**p-1)/(A-1)) mod M */ #ifndef LCG_LEAPFROG_ #define LCG_LEAPFROG_ #include #include #include #include const int64_t RANDMAX = (1LL << 48) - 1; // M = 2**48 */ const int64_t A = 25214903917L; const int64_t B = 11L; typedef struct rand_state { int p; int id; int64_t a; int64_t b; int64_t next_value; } rand_state_t; /* public functions -- declarations */ /* * function to initially generate modified constants a, b for * leapfrogging for p "streams" (one per process/thread) and * save "id" -- which stream this is, for use in setting seed */ void rand_init_state(int p, int id, rand_state_t *state); /* * function to set seed -- and I think this should generate the * first value for this stream, using the "id" stored in the state -- * i.e., it should skip to the id-th element of the original full * sequence */ void rand_set_seed(long seed, rand_state_t *state); /* * return current value and generate next */ int64_t rand_next(rand_state_t *state); /* internal functions */ /* make "mask" value of 2**exp - 1 (for computing m % 2**exp using &) */ static void make_mask(mpz_t mask, long exp) { mpz_t tmp; mpz_init_set_ui(tmp, 2); mpz_pow_ui(tmp, tmp, exp); mpz_sub_ui(tmp, tmp, 1); mpz_set(mask, tmp); } /* * functions to convert between int64_t and GMP multiprecision * integer: * * GMP has functions to convert to and from "long", but there's * no guarantee in C that that will be as big as int64_t. * my solution was to convert using a text string as an * intermediate value. To get the right conversion specification * for sprintf and sscanf, I think you have to use macros PRId64 * and SCNd64, defined in inttypes.h, and C string concatenation. */ static int64_t int64_from_mpz(mpz_t mpz) { /* * first get mpz mod 2**63 (maximum for 64-bit signed int), to * avoid overflow! */ mpz_t mask; mpz_init(mask); make_mask(mask, 63); mpz_t mpz_masked; mpz_init(mpz_masked); mpz_and(mpz_masked, mpz, mask); /* * now convert to 64-bit int using text string as intermediate step */ char buffer[1+gmp_snprintf(NULL, 0, "%Zd", mpz_masked)]; gmp_sprintf(buffer, "%Zd", mpz_masked); int64_t n; assert(sscanf(buffer, "%"SCNd64, &n) == 1); return n; } static void int64_to_mpz(mpz_t mpz, int64_t n) { char buffer[1+snprintf(NULL, 0, "%"PRId64, n)]; sprintf(buffer, "%"PRId64, n); assert(gmp_sscanf(buffer, "%Zd", mpz) == 1); } /* FIXME? you might want additional functions here */ /* public functions -- definitions */ void rand_init_state(int p, int id, rand_state_t *state) { assert((p > 0) && (id >= 0) && (id < p)); state->p = p; state->id = id; mpz_t A_p; mpz_init(A_p); int64_to_mpz(A_p, A); mpz_pow_ui(A_p, A_p, p); state->a = int64_from_mpz(A_p) & RANDMAX; mpz_t bwork; mpz_init(bwork); int64_to_mpz(bwork, B); mpz_t A_p_minus1; mpz_init(A_p_minus1); mpz_sub_ui(A_p_minus1, A_p, 1); mpz_mul(bwork, bwork, A_p_minus1); mpz_t Aminus1; mpz_init(Aminus1); int64_to_mpz(Aminus1, A-1); mpz_tdiv_q(bwork, bwork, Aminus1); state->b = int64_from_mpz(bwork) & RANDMAX; } void rand_set_seed(long seed, rand_state_t *state) { /* FIXME your code goes here */ } int64_t rand_next(rand_state_t *state) { /* FIXME your code goes here */ return 0; /* FIXME */ } #endif