/* * PCG64 Random Number Generation for C. * * Copyright 2014 Melissa O'Neill * Copyright 2015 Robert Kern * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * For additional information about the PCG random number generation scheme, * including its license and other licensing options, visit * * https://www.pcg-random.org * * Relicensed MIT in May 2019 * * The MIT License * * PCG Random Number Generation for C. * * Copyright 2014 Melissa O'Neill * * 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. */ #ifndef PCG64_H_INCLUDED #define PCG64_H_INCLUDED 1 #include #ifdef _WIN32 #include #endif #if defined(_WIN32) && !defined (__MINGW32__) #define inline __forceinline #endif #if defined(__GNUC_GNU_INLINE__) && !defined(__cplusplus) #error Nonstandard GNU inlining semantics. Compile with -std=c99 or better. #endif #ifdef __cplusplus extern "C" { #endif #if defined(__SIZEOF_INT128__) && !defined(PCG_FORCE_EMULATED_128BIT_MATH) typedef __uint128_t pcg128_t; #define PCG_128BIT_CONSTANT(high, low) (((pcg128_t)(high) << 64) + low) #else typedef struct { uint64_t high; uint64_t low; } pcg128_t; static inline pcg128_t PCG_128BIT_CONSTANT(uint64_t high, uint64_t low) { pcg128_t result; result.high = high; result.low = low; return result; } #define PCG_EMULATED_128BIT_MATH 1 #endif typedef struct { pcg128_t state; } pcg_state_128; typedef struct { pcg128_t state; pcg128_t inc; } pcg_state_setseq_128; #define PCG_DEFAULT_MULTIPLIER_HIGH 2549297995355413924ULL #define PCG_DEFAULT_MULTIPLIER_LOW 4865540595714422341ULL #define PCG_DEFAULT_MULTIPLIER_128 \ PCG_128BIT_CONSTANT(PCG_DEFAULT_MULTIPLIER_HIGH, PCG_DEFAULT_MULTIPLIER_LOW) #define PCG_DEFAULT_INCREMENT_128 \ PCG_128BIT_CONSTANT(6364136223846793005ULL, 1442695040888963407ULL) #define PCG_STATE_SETSEQ_128_INITIALIZER \ { \ PCG_128BIT_CONSTANT(0x979c9a98d8462005ULL, 0x7d3e9cb6cfe0549bULL) \ , PCG_128BIT_CONSTANT(0x0000000000000001ULL, 0xda3e39cb94b95bdbULL) \ } #define PCG_CHEAP_MULTIPLIER_128 (0xda942042e4dd58b5ULL) static inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot) { #ifdef _WIN32 return _rotr64(value, rot); #else return (value >> rot) | (value << ((-rot) & 63)); #endif } #ifdef PCG_EMULATED_128BIT_MATH static inline pcg128_t pcg128_add(pcg128_t a, pcg128_t b) { pcg128_t result; result.low = a.low + b.low; result.high = a.high + b.high + (result.low < b.low); return result; } static inline void _pcg_mult64(uint64_t x, uint64_t y, uint64_t *z1, uint64_t *z0) { #if defined _WIN32 && _M_AMD64 z0[0] = _umul128(x, y, z1); #else uint64_t x0, x1, y0, y1; uint64_t w0, w1, w2, t; /* Lower 64 bits are straightforward clock-arithmetic. */ *z0 = x * y; x0 = x & 0xFFFFFFFFULL; x1 = x >> 32; y0 = y & 0xFFFFFFFFULL; y1 = y >> 32; w0 = x0 * y0; t = x1 * y0 + (w0 >> 32); w1 = t & 0xFFFFFFFFULL; w2 = t >> 32; w1 += x0 * y1; *z1 = x1 * y1 + w2 + (w1 >> 32); #endif } static inline pcg128_t pcg128_mult(pcg128_t a, pcg128_t b) { uint64_t h1; pcg128_t result; h1 = a.high * b.low + a.low * b.high; _pcg_mult64(a.low, b.low, &(result.high), &(result.low)); result.high += h1; return result; } static inline void pcg_setseq_128_step_r(pcg_state_setseq_128 *rng) { rng->state = pcg128_add(pcg128_mult(rng->state, PCG_DEFAULT_MULTIPLIER_128), rng->inc); } static inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) { return pcg_rotr_64(state.high ^ state.low, state.high >> 58u); } static inline void pcg_setseq_128_srandom_r(pcg_state_setseq_128 *rng, pcg128_t initstate, pcg128_t initseq) { rng->state = PCG_128BIT_CONSTANT(0ULL, 0ULL); rng->inc.high = initseq.high << 1u; rng->inc.high |= initseq.low >> 63u; rng->inc.low = (initseq.low << 1u) | 1u; pcg_setseq_128_step_r(rng); rng->state = pcg128_add(rng->state, initstate); pcg_setseq_128_step_r(rng); } static inline uint64_t pcg_setseq_128_xsl_rr_64_random_r(pcg_state_setseq_128 *rng) { #if defined _WIN32 && _M_AMD64 uint64_t h1; pcg128_t product; /* Manually inline the multiplication and addition using intrinsics */ h1 = rng->state.high * PCG_DEFAULT_MULTIPLIER_LOW + rng->state.low * PCG_DEFAULT_MULTIPLIER_HIGH; product.low = _umul128(rng->state.low, PCG_DEFAULT_MULTIPLIER_LOW, &(product.high)); product.high += h1; _addcarry_u64(_addcarry_u64(0, product.low, rng->inc.low, &(rng->state.low)), product.high, rng->inc.high, &(rng->state.high)); return _rotr64(rng->state.high ^ rng->state.low, rng->state.high >> 58u); #else pcg_setseq_128_step_r(rng); return pcg_output_xsl_rr_128_64(rng->state); #endif } static inline pcg128_t pcg128_mult_64(pcg128_t a, uint64_t b) { uint64_t h1; pcg128_t result; h1 = a.high * b; _pcg_mult64(a.low, b, &(result.high), &(result.low)); result.high += h1; return result; } static inline void pcg_cm_step_r(pcg_state_setseq_128 *rng) { #if defined _WIN32 && _M_AMD64 uint64_t h1; pcg128_t product; /* Manually inline the multiplication and addition using intrinsics */ h1 = rng->state.high * PCG_CHEAP_MULTIPLIER_128; product.low = _umul128(rng->state.low, PCG_CHEAP_MULTIPLIER_128, &(product.high)); product.high += h1; _addcarry_u64(_addcarry_u64(0, product.low, rng->inc.low, &(rng->state.low)), product.high, rng->inc.high, &(rng->state.high)); #else rng->state = pcg128_add(pcg128_mult_64(rng->state, PCG_CHEAP_MULTIPLIER_128), rng->inc); #endif } static inline void pcg_cm_srandom_r(pcg_state_setseq_128 *rng, pcg128_t initstate, pcg128_t initseq) { rng->state = PCG_128BIT_CONSTANT(0ULL, 0ULL); rng->inc.high = initseq.high << 1u; rng->inc.high |= initseq.low >> 63u; rng->inc.low = (initseq.low << 1u) | 1u; pcg_cm_step_r(rng); rng->state = pcg128_add(rng->state, initstate); pcg_cm_step_r(rng); } static inline uint64_t pcg_cm_random_r(pcg_state_setseq_128* rng) { /* Lots of manual inlining to help out certain compilers to generate * performant code. */ uint64_t hi = rng->state.high; uint64_t lo = rng->state.low; /* Run the DXSM output function on the pre-iterated state. */ lo |= 1; hi ^= hi >> 32; hi *= 0xda942042e4dd58b5ULL; hi ^= hi >> 48; hi *= lo; /* Run the CM step. */ #if defined _WIN32 && _M_AMD64 uint64_t h1; pcg128_t product; /* Manually inline the multiplication and addition using intrinsics */ h1 = rng->state.high * PCG_CHEAP_MULTIPLIER_128; product.low = _umul128(rng->state.low, PCG_CHEAP_MULTIPLIER_128, &(product.high)); product.high += h1; _addcarry_u64(_addcarry_u64(0, product.low, rng->inc.low, &(rng->state.low)), product.high, rng->inc.high, &(rng->state.high)); #else rng->state = pcg128_add(pcg128_mult_64(rng->state, PCG_CHEAP_MULTIPLIER_128), rng->inc); #endif return hi; } #else /* PCG_EMULATED_128BIT_MATH */ static inline void pcg_setseq_128_step_r(pcg_state_setseq_128 *rng) { rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; } static inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) { return pcg_rotr_64(((uint64_t)(state >> 64u)) ^ (uint64_t)state, state >> 122u); } static inline void pcg_cm_step_r(pcg_state_setseq_128 *rng) { rng-> state = rng->state * PCG_CHEAP_MULTIPLIER_128 + rng->inc; } static inline uint64_t pcg_output_cm_128_64(pcg128_t state) { uint64_t hi = state >> 64; uint64_t lo = state; lo |= 1; hi ^= hi >> 32; hi *= 0xda942042e4dd58b5ULL; hi ^= hi >> 48; hi *= lo; return hi; } static inline void pcg_cm_srandom_r(pcg_state_setseq_128 *rng, pcg128_t initstate, pcg128_t initseq) { rng->state = 0U; rng->inc = (initseq << 1u) | 1u; pcg_cm_step_r(rng); rng->state += initstate; pcg_cm_step_r(rng); } static inline uint64_t pcg_cm_random_r(pcg_state_setseq_128* rng) { uint64_t ret = pcg_output_cm_128_64(rng->state); pcg_cm_step_r(rng); return ret; } static inline uint64_t pcg_setseq_128_xsl_rr_64_random_r(pcg_state_setseq_128* rng) { pcg_setseq_128_step_r(rng); return pcg_output_xsl_rr_128_64(rng->state); } static inline void pcg_setseq_128_srandom_r(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); } #endif /* PCG_EMULATED_128BIT_MATH */ static inline uint64_t pcg_setseq_128_xsl_rr_64_boundedrand_r(pcg_state_setseq_128 *rng, uint64_t bound) { uint64_t threshold = -bound % bound; for (;;) { uint64_t r = pcg_setseq_128_xsl_rr_64_random_r(rng); if (r >= threshold) return r % bound; } } extern pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, pcg128_t cur_plus); static inline void pcg_setseq_128_advance_r(pcg_state_setseq_128 *rng, pcg128_t delta) { rng->state = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, rng->inc); } static inline void pcg_cm_advance_r(pcg_state_setseq_128 *rng, pcg128_t delta) { rng->state = pcg_advance_lcg_128(rng->state, delta, PCG_128BIT_CONSTANT(0, PCG_CHEAP_MULTIPLIER_128), rng->inc); } typedef pcg_state_setseq_128 pcg64_random_t; #define pcg64_random_r pcg_setseq_128_xsl_rr_64_random_r #define pcg64_boundedrand_r pcg_setseq_128_xsl_rr_64_boundedrand_r #define pcg64_srandom_r pcg_setseq_128_srandom_r #define pcg64_advance_r pcg_setseq_128_advance_r #define PCG64_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER #ifdef __cplusplus } #endif typedef struct s_pcg64_state { pcg64_random_t *pcg_state; int has_uint32; uint32_t uinteger; } pcg64_state; static inline uint64_t pcg64_next64(pcg64_state *state) { return pcg64_random_r(state->pcg_state); } static inline uint32_t pcg64_next32(pcg64_state *state) { uint64_t next; if (state->has_uint32) { state->has_uint32 = 0; return state->uinteger; } next = pcg64_random_r(state->pcg_state); state->has_uint32 = 1; state->uinteger = (uint32_t)(next >> 32); return (uint32_t)(next & 0xffffffff); } static inline uint64_t pcg64_cm_next64(pcg64_state *state) { return pcg_cm_random_r(state->pcg_state); } static inline uint32_t pcg64_cm_next32(pcg64_state *state) { uint64_t next; if (state->has_uint32) { state->has_uint32 = 0; return state->uinteger; } next = pcg_cm_random_r(state->pcg_state); state->has_uint32 = 1; state->uinteger = (uint32_t)(next >> 32); return (uint32_t)(next & 0xffffffff); } void pcg64_advance(pcg64_state *state, uint64_t *step); void pcg64_cm_advance(pcg64_state *state, uint64_t *step); void pcg64_set_seed(pcg64_state *state, uint64_t *seed, uint64_t *inc); void pcg64_get_state(pcg64_state *state, uint64_t *state_arr, int *has_uint32, uint32_t *uinteger); void pcg64_set_state(pcg64_state *state, uint64_t *state_arr, int has_uint32, uint32_t uinteger); #endif /* PCG64_H_INCLUDED */