/* 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. To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #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))); } }