/* Written in 2019 by Sebastiano Vigna (vigna@acm.org) Initializes two PCG generators on different arbitrary streams. You choose the state of the first stream: the program finds another starting state for the second stream, and from there onwards the difference between the state of the two generators will always be the same. In other words: there are no multiple streams. Modulo an additive constant, there are just two streams: odd initializers, and even initializers. Durst's 1989 paper contains a detailed explanation https://ieeexplore.ieee.org/document/718715. See . */ #include #include #include #include #include #include typedef __uint128_t uint128_t; // ------- // PCG code Copyright by Melissa O'Neill typedef __uint128_t pcg128_t; #define PCG_128BIT_CONSTANT(high,low) ((((pcg128_t)high) << 64) + low) struct pcg_state_setseq_128 { pcg128_t state; pcg128_t inc; }; static inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot) { return (value >> rot) | (value << ((- rot) & 127)); } #define PCG_DEFAULT_MULTIPLIER_128 \ PCG_128BIT_CONSTANT(2549297995355413924ULL,4865540595714422341ULL) static inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng) { rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; } static inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state) { return (uint64_t)(((state >> 43u) ^ state) >> ((state >> 124u) + 45u)); } static inline uint64_t pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng) { pcg_setseq_128_step_r(rng); return pcg_output_xsh_rs_128_64(rng->state); } static inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, pcg128_t initstate, pcg128_t initseq) { rng->state = 0U; rng->inc = (initseq << 1u) | 1u; pcg_setseq_128_step_r(rng); rng->state += initstate; pcg_setseq_128_step_r(rng); } static uint64_t _strtoull(char *s) { const uint64_t result = strtoull(s, NULL, 0); if (errno) { fprintf(stderr, "%s\n", strerror(errno)); exit(1); } return result; } int main(int argc, char *argv[]) { if (argc != 7) { fprintf(stderr, "USAGE: %s STATE_UPPER_BITS STATE_LOWER_BITS C_UPPER_BITS C_LOWER_BITS D_UPPER_BITS D_LOWER_BITS\n", argv[0]); exit(1); } // Initial provided state const uint128_t state = (uint128_t)_strtoull(argv[1]) << 64 | _strtoull(argv[2]); // First stream const uint128_t c = (uint128_t)_strtoull(argv[3]) << 64 | _strtoull(argv[4]); // Second stream const uint128_t d = (uint128_t)_strtoull(argv[5]) << 64 | _strtoull(argv[6]); if (c % 2 != d % 2) { fprintf(stderr, "C and D must have the same parity (both even or both odd)\n"); return 1; } const uint128_t r = ((uint128_t)0x335b297a369e47e6 << 64 | 0x63c87867f9472371) * ((d - c) / 2); struct pcg_state_setseq_128 rng0, rng1; // Start first generator (constant c) from state pcg_setseq_128_srandom_r(&rng0, state, c); // Start second generator (constant d) from suitable state pcg_setseq_128_srandom_r(&rng1, state + (c << 1 | 1) - (d << 1 | 1) - r, d); // Print difference between PRNG states for(int i = 0; i < 16; i++) { pcg_setseq_128_xsh_rs_64_random_r(&rng0); pcg_setseq_128_xsh_rs_64_random_r(&rng1); printf("0x%016" PRIx64 "%016" PRIx64 "\n", (uint64_t)((rng0.state - rng1.state) >> 64), (uint64_t)((rng0.state - rng1.state))); } }