This was actually a retooling of the one from my bc. Signed-off-by: Gavin Howard <gavin@yzena.com>master
parent
f7208ff080
commit
dd1dc708b2
@ -0,0 +1,246 @@
|
||||
/**
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
*
|
||||
* Copyright 2017-2023 Yzena, LLC
|
||||
*
|
||||
* Licensed under the Yzena Viral User License, Version 0.1 (the "Yzena Viral
|
||||
* User License" or "YVUL"), the GNU Affero General Public License (the "GNU
|
||||
* AGPL"), Version 3.0, and the Server Side Public License (the "SSPL"),
|
||||
* Version 1. You may not use this file except in compliance with all of those
|
||||
* licenses.
|
||||
*
|
||||
* You may obtain a copy of the Yzena Viral User License at
|
||||
*
|
||||
* https://yzena.com/yzena-viral-user-license/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Yzena Viral User License is distributed under the
|
||||
* following disclaimer:
|
||||
*
|
||||
* As far as the law allows, this software comes as is, without any
|
||||
* warranty or condition, and no contributor will be liable to anyone for
|
||||
* any damages related to this software or this license, under any kind of
|
||||
* legal claim.
|
||||
*
|
||||
* You may obtain a copy of the GNU Affero General Public License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the GNU Affero General Public License is distributed under
|
||||
* the following disclaimer:
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You may obtain a copy of the Server Side Public License at
|
||||
*
|
||||
* https://www.mongodb.com/licensing/server-side-public-license
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Server Side Public License is distributed under the
|
||||
* following disclaimer:
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Server
|
||||
* Side Public License for more details.
|
||||
*
|
||||
* *****************************************************************************
|
||||
*
|
||||
* Parts of this code are adapted from the following:
|
||||
*
|
||||
* PCG, A Family of Better Random Number Generators.
|
||||
*
|
||||
* You can find the original source code at:
|
||||
* https://github.com/imneme/pcg-c
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* *****************************************************************************
|
||||
*
|
||||
* ****** END LICENSE BLOCK ******
|
||||
*
|
||||
* *****************************************************************
|
||||
*
|
||||
* ******* BEGIN FILE DESCRIPTION *******
|
||||
*
|
||||
* Public declarations for Yc's non-cryptographically-secure (seeded)
|
||||
* pseudo-random number generator.
|
||||
*
|
||||
* ******** END FILE DESCRIPTION ********
|
||||
*/
|
||||
|
||||
#ifndef YC_RAND_H
|
||||
#define YC_RAND_H
|
||||
|
||||
// For C++ compatibility.
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <yc/arith.h>
|
||||
#include <yc/yc.h>
|
||||
|
||||
/**
|
||||
* @file yc/rand.h
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup rand rand
|
||||
* Public declarations for Yc's non-cryptographically-secure (seeded)
|
||||
* pseudo-random number generator.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if YC_CLANG
|
||||
#pragma clang diagnostic ignored "-Wpadded"
|
||||
#endif // YC_CLANG
|
||||
|
||||
#if YC_64BIT
|
||||
|
||||
/**
|
||||
* The type for random integers.
|
||||
*/
|
||||
typedef uint64_t y_RandInt;
|
||||
|
||||
#if !YC_INT128_STRUCT
|
||||
|
||||
/**
|
||||
* A typedef for the PRNG state.
|
||||
*/
|
||||
typedef uint128_t y_RandState;
|
||||
|
||||
#else // !YC_INT128_STRUCT
|
||||
|
||||
/// A typedef for the PCG state.
|
||||
typedef struct y_RandState
|
||||
{
|
||||
/// The low bits.
|
||||
uint_fast64_t lo;
|
||||
|
||||
/// The high bits.
|
||||
uint_fast64_t hi;
|
||||
|
||||
} y_RandState;
|
||||
|
||||
#endif // !YC_INT128_STRUCT
|
||||
|
||||
#else // YC_64BIT
|
||||
|
||||
/**
|
||||
* The type for random integers.
|
||||
*/
|
||||
typedef uint32_t RandInt;
|
||||
|
||||
/**
|
||||
* A typedef for the PRNG state.
|
||||
*/
|
||||
typedef uint_fast64_t y_RandState;
|
||||
|
||||
#endif // YC_64BIT
|
||||
|
||||
/**
|
||||
* The actual RNG data. These are the actual PRNG's.
|
||||
*/
|
||||
typedef struct y_PRNG
|
||||
{
|
||||
/// The state.
|
||||
y_RandState state;
|
||||
|
||||
/// The increment and the modified bit.
|
||||
y_RandState inc;
|
||||
|
||||
} y_PRNG;
|
||||
|
||||
/**
|
||||
* Creates a new PRNG.
|
||||
* @return A new PRNG, already seeded.
|
||||
*/
|
||||
y_PRNG
|
||||
y_rand_create(void) y_nodiscard;
|
||||
|
||||
/**
|
||||
* Creates a new PRNG with the given seed.
|
||||
* @param state1 The first part of the state.
|
||||
* @param state2 The second part of the state.
|
||||
* @param inc1 The first part of the increment.
|
||||
* @param inc2 The second part of the increment.
|
||||
* @return A new PRNG, already seeded.
|
||||
*/
|
||||
y_PRNG
|
||||
y_rand_create_seed(y_ulong state1, y_ulong state2, y_ulong inc1, y_ulong inc2) y_nodiscard;
|
||||
|
||||
/**
|
||||
* Returns a random integer from the PRNG.
|
||||
* @param r The PRNG.
|
||||
* @return A random integer.
|
||||
*/
|
||||
y_RandInt
|
||||
y_rand_int(y_PRNG* r) y_allnonnull y_nodiscard;
|
||||
|
||||
/**
|
||||
* Returns a random integer from the PRNG bounded by @a bound. Bias is
|
||||
* eliminated.
|
||||
* @param r The PRNG.
|
||||
* @param bound The bound for the random integer.
|
||||
* @return A bounded random integer.
|
||||
*/
|
||||
y_RandInt
|
||||
y_rand_bounded(y_PRNG* r, y_RandInt bound) y_allnonnull y_nodiscard;
|
||||
|
||||
/**
|
||||
* Seed the PRNG with the state in two parts and the increment in two parts.
|
||||
* @param r The PRNG.
|
||||
* @param state1 The first part of the state.
|
||||
* @param state2 The second part of the state.
|
||||
* @param inc1 The first part of the increment.
|
||||
* @param inc2 The second part of the increment.
|
||||
*/
|
||||
void
|
||||
y_rand_seed(y_PRNG* r, y_ulong state1, y_ulong state2, y_ulong inc1, y_ulong inc2);
|
||||
|
||||
/**
|
||||
* Returns, via pointers, the state of the PRNG in pieces.
|
||||
* @param r The PRNG.
|
||||
* @param s1 The return value for the first part of the state.
|
||||
* @param s2 The return value for the second part of the state.
|
||||
* @param i1 The return value for the first part of the increment.
|
||||
* @param i2 The return value for the second part of the increment.
|
||||
*/
|
||||
void
|
||||
y_rand_getRands(y_PRNG* r, y_RandInt* s1, y_RandInt* s2, y_RandInt* i1,
|
||||
y_RandInt* i2) y_allnonnull;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // YC_RAND_H
|
@ -0,0 +1,586 @@
|
||||
/**
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
*
|
||||
* Copyright 2017-2023 Yzena, LLC
|
||||
*
|
||||
* Licensed under the Yzena Viral User License, Version 0.1 (the "Yzena Viral
|
||||
* User License" or "YVUL"), the GNU Affero General Public License (the "GNU
|
||||
* AGPL"), Version 3.0, and the Server Side Public License (the "SSPL"),
|
||||
* Version 1. You may not use this file except in compliance with all of those
|
||||
* licenses.
|
||||
*
|
||||
* You may obtain a copy of the Yzena Viral User License at
|
||||
*
|
||||
* https://yzena.com/yzena-viral-user-license/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Yzena Viral User License is distributed under the
|
||||
* following disclaimer:
|
||||
*
|
||||
* As far as the law allows, this software comes as is, without any
|
||||
* warranty or condition, and no contributor will be liable to anyone for
|
||||
* any damages related to this software or this license, under any kind of
|
||||
* legal claim.
|
||||
*
|
||||
* You may obtain a copy of the GNU Affero General Public License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the GNU Affero General Public License is distributed under
|
||||
* the following disclaimer:
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You may obtain a copy of the Server Side Public License at
|
||||
*
|
||||
* https://www.mongodb.com/licensing/server-side-public-license
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Server Side Public License is distributed under the
|
||||
* following disclaimer:
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Server
|
||||
* Side Public License for more details.
|
||||
*
|
||||
* *****************************************************************************
|
||||
*
|
||||
* Parts of this code are adapted from the following:
|
||||
*
|
||||
* PCG, A Family of Better Random Number Generators.
|
||||
*
|
||||
* You can find the original source code at:
|
||||
* https://github.com/imneme/pcg-c
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* *****************************************************************************
|
||||
*
|
||||
* ****** END LICENSE BLOCK ******
|
||||
*
|
||||
* *****************************************************************
|
||||
*
|
||||
* ******* BEGIN FILE DESCRIPTION *******
|
||||
*
|
||||
* Definitions for Yc's non-cryptographically-secure (seeded) pseudo-random
|
||||
* number generator.
|
||||
*
|
||||
* ******** END FILE DESCRIPTION ********
|
||||
*/
|
||||
|
||||
#include <yc/rand.h>
|
||||
#include <yc/sys.h>
|
||||
|
||||
#include "rand.h"
|
||||
|
||||
#include "../concurrency/strucon.h"
|
||||
|
||||
/// A constant for the rand multiplier.
|
||||
static const y_RandState y_rand_multiplier = y_RAND_MULTIPLIER;
|
||||
|
||||
#if YC_INT128_STRUCT
|
||||
|
||||
/**
|
||||
* Adds two 64-bit values and preserves the overflow.
|
||||
* @param a The first operand.
|
||||
* @param b The second operand.
|
||||
* @return The sum, including overflow.
|
||||
*/
|
||||
static y_RandState
|
||||
y_rand_addition(uint_fast64_t a, uint_fast64_t b)
|
||||
{
|
||||
y_RandState res;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
res.lo = a + b;
|
||||
res.hi = (res.lo < a);
|
||||
|
||||
y_return_dbg(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two 128-bit values and discards the overflow.
|
||||
* @param a The first operand.
|
||||
* @param b The second operand.
|
||||
* @return The sum, without overflow.
|
||||
*/
|
||||
static y_RandState
|
||||
y_rand_addition2(y_RandState a, y_RandState b)
|
||||
{
|
||||
y_RandState temp;
|
||||
y_RandState res;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
res = y_rand_addition(a.lo, b.lo);
|
||||
temp = y_rand_addition(a.hi, b.hi);
|
||||
res.hi += temp.lo;
|
||||
|
||||
y_return_dbg(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two 64-bit values and preserves the overflow.
|
||||
* @param a The first operand.
|
||||
* @param b The second operand.
|
||||
* @return The product, including overflow.
|
||||
*/
|
||||
static y_RandState
|
||||
y_rand_multiply(uint_fast64_t a, uint_fast64_t b)
|
||||
{
|
||||
uint_fast64_t al;
|
||||
uint_fast64_t ah;
|
||||
uint_fast64_t bl;
|
||||
uint_fast64_t bh;
|
||||
uint_fast64_t c0;
|
||||
uint_fast64_t c1;
|
||||
uint_fast64_t c2;
|
||||
uint_fast64_t c3;
|
||||
y_RandState carry;
|
||||
y_RandState res;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
al = y_RAND_TRUNC32(a);
|
||||
ah = y_RAND_CHOP32(a);
|
||||
bl = y_RAND_TRUNC32(b);
|
||||
bh = y_RAND_CHOP32(b);
|
||||
|
||||
c0 = al * bl;
|
||||
c1 = al * bh;
|
||||
c2 = ah * bl;
|
||||
c3 = ah * bh;
|
||||
|
||||
carry = y_rand_addition(c1, c2);
|
||||
|
||||
res = y_rand_addition(c0, (y_RAND_TRUNC32(carry.lo)) << 32);
|
||||
res.hi += y_RAND_CHOP32(carry.lo) + c3 + (carry.hi << 32);
|
||||
|
||||
y_return_dbg(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two 128-bit values and discards the overflow.
|
||||
* @param a The first operand.
|
||||
* @param b The second operand.
|
||||
* @return The product, without overflow.
|
||||
*/
|
||||
static y_RandState
|
||||
y_rand_multiply2(y_RandState a, y_RandState b)
|
||||
{
|
||||
y_RandState c0;
|
||||
y_RandState c1;
|
||||
y_RandState c2;
|
||||
y_RandState carry;
|
||||
y_RandState ret;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
c0 = y_rand_multiply(a.lo, b.lo);
|
||||
c1 = y_rand_multiply(a.lo, b.hi);
|
||||
c2 = y_rand_multiply(a.hi, b.lo);
|
||||
|
||||
carry = y_rand_addition2(c1, c2);
|
||||
carry.hi = carry.lo;
|
||||
carry.lo = 0;
|
||||
|
||||
ret = y_rand_addition2(c0, carry);
|
||||
|
||||
y_return_dbg(ret);
|
||||
}
|
||||
|
||||
#endif // YC_INT128_STRUCT
|
||||
|
||||
/**
|
||||
* Returns a random @a y_ulong using the system CSPRNG.
|
||||
* @param ret A pointer to return the @a y_ulong.
|
||||
* @return An error code, if any.
|
||||
* @pre @a ret must not be NULL.
|
||||
*/
|
||||
static y_Status
|
||||
y_rand_sysrand(y_ulong* ret) y_allnonnull y_nodiscard;
|
||||
|
||||
static y_Status
|
||||
y_rand_sysrand(y_ulong* ret)
|
||||
{
|
||||
y_Status s;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
s = y_sys_genrandom(ret, sizeof(y_ulong));
|
||||
|
||||
y_return_dbg(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads random data from rand(), byte-by-byte because rand() is only guaranteed
|
||||
* to return 15 bits of random data. This is the final fallback and is not
|
||||
* preferred as it is possible to access cryptographically-secure PRNG's on most
|
||||
* systems.
|
||||
* @return The random data as an unsigned long.
|
||||
*/
|
||||
static y_ulong
|
||||
y_rand_rand(void) y_nodiscard;
|
||||
|
||||
static y_ulong
|
||||
y_rand_rand(void)
|
||||
{
|
||||
size_t i;
|
||||
y_ulong res;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
res = 0;
|
||||
|
||||
// Fill up the unsigned long byte-by-byte.
|
||||
for (i = 0; i < sizeof(y_ulong); ++i)
|
||||
{
|
||||
res |= ((y_ulong) (rand() & y_RAND_SRAND_BITS)) << (i * CHAR_BIT);
|
||||
}
|
||||
|
||||
y_return_dbg(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual increment of the PRNG, including the required last odd
|
||||
* bit.
|
||||
* @param r The PRNG.
|
||||
* @return The increment of the PRNG, including the last odd bit.
|
||||
*/
|
||||
static inline y_RandState
|
||||
y_rand_inc(y_PRNG* r) y_allnonnull y_inline;
|
||||
|
||||
static y_RandState
|
||||
y_rand_inc(y_PRNG* r)
|
||||
{
|
||||
y_RandState inc;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
y_RAND_INC(r, inc);
|
||||
|
||||
y_return_dbg(inc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the increment for the PRNG.
|
||||
* @param r The PRNG whose increment will be set up.
|
||||
*/
|
||||
static inline void
|
||||
y_rand_setupInc(y_PRNG* r) y_allnonnull y_inline;
|
||||
|
||||
static void
|
||||
y_rand_setupInc(y_PRNG* r)
|
||||
{
|
||||
y_call_dbg();
|
||||
|
||||
y_RAND_SETUPINC(r);
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeds the state of a PRNG.
|
||||
* @param state The return parameter; the state to seed.
|
||||
* @param val1 The lower half of the state.
|
||||
* @param val2 The upper half of the state.
|
||||
*/
|
||||
static void
|
||||
y_rand_seedState(y_RandState* state, y_ulong val1, y_ulong val2) y_allnonnull y_inline;
|
||||
|
||||
static void
|
||||
y_rand_seedState(y_RandState* state, y_ulong val1, y_ulong val2)
|
||||
{
|
||||
y_call_dbg();
|
||||
|
||||
y_RAND_SEEDSTATE(state, val1, val2);
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeds a PRNG.
|
||||
* @param r The return parameter; the PRNG to seed.
|
||||
* @param state1 The lower half of the state.
|
||||
* @param state2 The upper half of the state.
|
||||
* @param inc1 The lower half of the increment.
|
||||
* @param inc2 The upper half of the increment.
|
||||
*/
|
||||
static inline void
|
||||
y_rand_seedRNG(y_PRNG* r, y_ulong state1, y_ulong state2, y_ulong inc1,
|
||||
y_ulong inc2) y_allnonnull y_inline;
|
||||
|
||||
static void
|
||||
y_rand_seedRNG(y_PRNG* r, y_ulong state1, y_ulong state2, y_ulong inc1,
|
||||
y_ulong inc2)
|
||||
{
|
||||
y_call_dbg();
|
||||
|
||||
y_rand_seedState(&r->state, state1, state2);
|
||||
y_rand_seedState(&r->inc, inc1, inc2);
|
||||
y_rand_setupInc(r);
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the "step" portion of a PCG udpate.
|
||||
* @param r The PRNG.
|
||||
*/
|
||||
static inline void
|
||||
y_rand_step(y_PRNG* r) y_allnonnull y_inline;
|
||||
|
||||
static inline void
|
||||
y_rand_step(y_PRNG* r)
|
||||
{
|
||||
y_RandState temp;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
temp = y_rand_mul2(r->state, y_rand_multiplier);
|
||||
|
||||
r->state = y_rand_add2(temp, y_rand_inc(r));
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new output of PCG.
|
||||
* @param r The PRNG.
|
||||
* @return The new output from the PRNG.
|
||||
*/
|
||||
static inline y_RandInt
|
||||
y_rand_output(y_PRNG* r)
|
||||
{
|
||||
y_RandInt ret;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
ret = y_RAND_ROT(y_RAND_FOLD(r->state), y_RAND_ROTAMT(r->state));
|
||||
|
||||
y_return_dbg(ret);
|
||||
}
|
||||
|
||||
void
|
||||
y_rand_srand(y_PRNG* rng)
|
||||
{
|
||||
y_Status s;
|
||||
y_ulong state1;
|
||||
y_ulong state2;
|
||||
y_ulong inc1;
|
||||
y_ulong inc2;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
s = y_rand_sysrand(&state1);
|
||||
if (y_err(s != y_STATUS_SUCCESS))
|
||||
{
|
||||
state1 = 0;
|
||||
}
|
||||
|
||||
s = y_rand_sysrand(&state2);
|
||||
if (y_err(s != y_STATUS_SUCCESS))
|
||||
{
|
||||
state2 = 0;
|
||||
}
|
||||
|
||||
s = y_rand_sysrand(&inc1);
|
||||
if (y_err(s != y_STATUS_SUCCESS))
|
||||
{
|
||||
inc1 = 0;
|
||||
}
|
||||
|
||||
s = y_rand_sysrand(&inc2);
|
||||
if (y_err(s != y_STATUS_SUCCESS))
|
||||
{
|
||||
inc2 = 0;
|
||||
}
|
||||
|
||||
y_rand_seedRNG(rng, state1, state2, inc1, inc2);
|
||||
|
||||
// Fallback to rand() until the thing is seeded.
|
||||
while (y_err(y_RAND_ZERO(rng)))
|
||||
{
|
||||
if (state1 == 0)
|
||||
{
|
||||
state1 = y_rand_rand();
|
||||
}
|
||||
if (state2 == 0)
|
||||
{
|
||||
state2 = y_rand_rand();
|
||||
}
|
||||
if (inc1 == 0)
|
||||
{
|
||||
inc1 = y_rand_rand();
|
||||
}
|
||||
if (inc2 == 0)
|
||||
{
|
||||
inc2 = y_rand_rand();
|
||||
}
|
||||
|
||||
y_rand_seedRNG(rng, state1, state2, inc1, inc2);
|
||||
}
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
y_RandInt
|
||||
y_rand_int(y_PRNG* r)
|
||||
{
|
||||
y_RandInt res;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
y_assert(!y_RAND_ZERO(r), "PRNG is not seeded");
|
||||
|
||||
// This is the important part of the PRNG. This is the stuff from PCG.
|
||||
y_rand_step(r);
|
||||
res = y_rand_output(r);
|
||||
|
||||
y_return_dbg(res);
|
||||
}
|
||||
|
||||
y_RandInt
|
||||
y_rand_bounded(y_PRNG* r, y_RandInt bound)
|
||||
{
|
||||
// Calculate the threshold below which we have to try again.
|
||||
y_RandInt rand;
|
||||
y_RandInt threshold;
|
||||
y_RandInt ret;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
threshold = (0 - bound) % bound;
|
||||
|
||||
do
|
||||
{
|
||||
rand = y_rand_int(r);
|
||||
}
|
||||
while (rand < threshold);
|
||||
|
||||
ret = rand % bound;
|
||||
|
||||
y_return_dbg(ret);
|
||||
}
|
||||
|
||||
void
|
||||
y_rand_seed(y_PRNG* r, y_ulong state1, y_ulong state2, y_ulong inc1, y_ulong inc2)
|
||||
{
|
||||
y_call_dbg();
|
||||
|
||||
// Seed and set up the PRNG's increment.
|
||||
y_rand_seedState(&r->inc, inc1, inc2);
|
||||
y_rand_setupInc(r);
|
||||
|
||||
// If the state is 0, use the increment as the state. Otherwise, seed it
|
||||
// with the state.
|
||||
if (!state1 && !state2)
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
memcpy(&r->state, &r->inc, sizeof(y_RandState));
|
||||
y_rand_step(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
y_rand_seedState(&r->state, state1, state2);
|
||||
}
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the increment in the PRNG *without* the odd bit and also with being
|
||||
* shifted one bit down.
|
||||
* @param r The PRNG.
|
||||
* @return The increment without the odd bit and with being shifted one bit
|
||||
* down.
|
||||
*/
|
||||
static inline y_RandState
|
||||
y_rand_getInc(y_PRNG* r) y_allnonnull y_nodiscard y_inline;
|
||||
|
||||
static inline y_RandState
|
||||
y_rand_getInc(y_PRNG* r)
|
||||
{
|
||||
y_RandState res;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
y_assert(!y_RAND_ZERO(r), "PRNG is not seeded");
|
||||
|
||||
y_RAND_GETINC(r, res);
|
||||
|
||||
y_return_dbg(res);
|
||||
}
|
||||
|
||||
void
|
||||
y_rand_getRands(y_PRNG* r, y_RandInt* s1, y_RandInt* s2, y_RandInt* i1, y_RandInt* i2)
|
||||
{
|
||||
y_RandState inc;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
y_assert(!y_RAND_ZERO(r), "PRNG is not seeded");
|
||||
|
||||
// Get the increment.
|
||||
inc = y_rand_getInc(r);
|
||||
|
||||
// Chop the state.
|
||||
*s1 = y_RAND_TRUNC(r->state);
|
||||
*s2 = y_RAND_CHOP(r->state);
|
||||
|
||||
// Chop the increment.
|
||||
*i1 = y_RAND_TRUNC(inc);
|
||||
*i2 = y_RAND_CHOP(inc);
|
||||
|
||||
y_return_void_dbg;
|
||||
}
|
||||
|
||||
y_PRNG
|
||||
y_rand_create(void)
|
||||
{
|
||||
y_PRNG ret;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
y_rand_srand(&ret);
|
||||
|
||||
y_return_dbg(ret);
|
||||
}
|
||||
|
||||
y_PRNG
|
||||
y_rand_create_seed(y_ulong state1, y_ulong state2, y_ulong inc1, y_ulong inc2)
|
||||
{
|
||||
y_PRNG ret;
|
||||
|
||||
y_call_dbg();
|
||||
|
||||
y_rand_seed(&ret, state1, state2, inc1, inc2);
|
||||
|
||||
y_return_dbg(ret);
|
||||
}
|
@ -0,0 +1,643 @@
|
||||
/**
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
*
|
||||
* Copyright 2017-2023 Yzena, LLC
|
||||
*
|
||||
* Licensed under the Yzena Viral User License, Version 0.1 (the "Yzena Viral
|
||||
* User License" or "YVUL"), the GNU Affero General Public License (the "GNU
|
||||
* AGPL"), Version 3.0, and the Server Side Public License (the "SSPL"),
|
||||
* Version 1. You may not use this file except in compliance with all of those
|
||||
* licenses.
|
||||
*
|
||||
* You may obtain a copy of the Yzena Viral User License at
|
||||
*
|
||||
* https://yzena.com/yzena-viral-user-license/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Yzena Viral User License is distributed under the
|
||||
* following disclaimer:
|
||||
*
|
||||
* As far as the law allows, this software comes as is, without any
|
||||
* warranty or condition, and no contributor will be liable to anyone for
|
||||
* any damages related to this software or this license, under any kind of
|
||||
* legal claim.
|
||||
*
|
||||
* You may obtain a copy of the GNU Affero General Public License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the GNU Affero General Public License is distributed under
|
||||
* the following disclaimer:
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You may obtain a copy of the Server Side Public License at
|
||||
*
|
||||
* https://www.mongodb.com/licensing/server-side-public-license
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Server Side Public License is distributed under the
|
||||
* following disclaimer:
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Server
|
||||
* Side Public License for more details.
|
||||
*
|
||||
* *****************************************************************************
|
||||
*
|
||||
* Parts of this code are adapted from the following:
|
||||
*
|
||||
* PCG, A Family of Better Random Number Generators.
|
||||
*
|
||||
* You can find the original source code at:
|
||||
* https://github.com/imneme/pcg-c
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* *****************************************************************************
|
||||
*
|
||||
* ****** END LICENSE BLOCK ******
|
||||
*
|
||||
* *****************************************************************
|
||||
*
|
||||
* ******* BEGIN FILE DESCRIPTION *******
|
||||
*
|
||||
* Private declarations for Yc's non-cryptographically-secure (seeded)
|
||||
* pseudo-random number generator.
|
||||
*
|
||||
* ******** END FILE DESCRIPTION ********
|
||||
*/
|
||||
|
||||
#ifndef YC_RAND_PRIVATE_H
|
||||
#define YC_RAND_PRIVATE_H
|
||||
|
||||
// For C++ compatibility.
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <yc/arith.h>
|
||||
#include <yc/rand.h>
|
||||
|
||||
//! @cond INTERNAL
|
||||
|
||||
/**
|
||||
* @file src/util/rand.h
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup rand_internal rand_internal
|
||||
* Private declarations for Yc's non-cryptographically-secure (seeded)
|
||||
* pseudo-random number generator.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if YC_CLANG
|
||||
#pragma clang diagnostic ignored "-Wpadded"
|
||||
#endif // YC_CLANG
|
||||
|
||||
#if YC_64BIT
|
||||
|
||||
/**
|
||||
* @def y_RAND_ROTC
|
||||
* A constant defined by PCG.
|
||||
*/
|
||||
#define y_RAND_ROTC (63)
|
||||
|
||||
#if !YC_INT128_STRUCT
|
||||
|
||||
/**
|
||||
* Multiply two integers, worrying about overflow.
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @return The product of the PCG states.
|
||||
*/
|
||||
#define y_rand_mul(a, b) (((y_RandState) (a)) * ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Add two integers, worrying about overflow.
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @return The sum of the PCG states.
|
||||
*/
|
||||
#define y_rand_add(a, b) (((y_RandState) (a)) + ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Multiply two PCG states.
|
||||
* @param a The first PCG state.
|
||||
* @param b The second PCG state.
|
||||
* @return The product of the PCG states.
|
||||
*/
|
||||
#define y_rand_mul2(a, b) (((y_RandState) (a)) * ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Add two PCG states.
|
||||
* @param a The first PCG state.
|
||||
* @param b The second PCG state.
|
||||
* @return The sum of the PCG states.
|
||||
*/
|
||||
#define y_rand_add2(a, b) (((y_RandState) (a)) + ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Figure out if the PRNG has been modified. Since the increment of the PRNG has
|
||||
* to be odd, we use the extra bit to store whether it has been modified or not.
|
||||
* @param r The PRNG.
|
||||
* @return True if the PRNG has *not* been modified, false otherwise.
|
||||
*/
|
||||
#define y_RAND_NOTMODIFIED(r) (((r)->inc & 1UL) == 0)
|
||||
|
||||
/**
|
||||
* Return true if the PRNG has not been seeded yet.
|
||||
* @param r The PRNG.
|
||||
* @return True if the PRNG has not been seeded yet, false otherwise.
|
||||
*/
|
||||
#define y_RAND_ZERO(r) (!(r)->state)
|
||||
|
||||
/**
|
||||
* Returns a constant built from @a h and @a l.
|
||||
* @param h The high 64 bits.
|
||||
* @param l The low 64 bits.
|
||||
* @return The constant built from @a h and @a l.
|
||||
*/
|
||||
#define y_RAND_CONSTANT(h, l) ((((y_RandState) (h)) << 64) + (y_RandState) (l))
|
||||
|
||||
/**
|
||||
* Truncates a PCG state to the number of bits in a random integer.
|
||||
* @param s The state to truncate.
|
||||
* @return The truncated state.
|
||||
*/
|
||||
#define y_RAND_TRUNC(s) ((uint64_t) (s))
|
||||
|
||||
/**
|
||||
* Chops a PCG state in half and returns the top bits.
|
||||
* @param s The state to chop.
|
||||
* @return The chopped state's top bits.
|
||||
*/
|
||||
#define y_RAND_CHOP(s) ((uint64_t) ((s) >> 64UL))
|
||||
|
||||
/**
|
||||
* Rotates a PCG state.
|
||||
* @param s The state to rotate.
|
||||
* @return The rotated state.
|
||||
*/
|
||||
#define y_RAND_ROTAMT(s) ((unsigned int) ((s) >> 122UL))
|
||||
|
||||
/**
|
||||
* @def y_RAND_INC
|
||||
* Sets an increment.
|
||||
* @param r The PRNG data.
|
||||
* @param i The increment to set.
|
||||
*/
|
||||
#define y_RAND_INC(r, i) \
|
||||
do \
|
||||
{ \
|
||||
i = (r)->inc | 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SETUPINC
|
||||
* Sets up the increment value.
|
||||
* @param r The PRNG data.
|
||||
*/
|
||||
#define y_RAND_SETUPINC(r) \
|
||||
do \
|
||||
{ \
|
||||
(r)->inc <<= 1UL; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SEEDSTATE
|
||||
* Seeds the given state with the values.
|
||||
* @param s The state to seed.
|
||||
* @param v1 The first value.
|
||||
* @param v2 The second value.
|
||||
*/
|
||||
#define y_RAND_SEEDSTATE(s, v1, v2) \
|
||||
do \
|
||||
{ \
|
||||
*(s) = ((y_RandState) (v1)) | ((y_RandState) (v2)) << (64); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* Gets the increment value from the PRNG state.
|
||||
* @param r The PRNG state.
|
||||
* @param i The variable to set to the increment.
|
||||
*/
|
||||
#define y_RAND_GETINC(r, i) \
|
||||
do \
|
||||
{ \
|
||||
i = (r)->inc >> 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#else // !YC_INT128_STRUCT
|
||||
|
||||
/**
|
||||
* Multiply two integers, worrying about overflow.
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @return The product of the PCG states.
|
||||
*/
|
||||
#define y_rand_mul(a, b) (y_rand_multiply((a), (b)))
|
||||
|
||||
/**
|
||||
* Add two integers, worrying about overflow.
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @return The sum of the PCG states.
|
||||
*/
|
||||
#define y_rand_add(a, b) (y_rand_addition((a), (b)))
|
||||
|
||||
/**
|
||||
* Multiply two PCG states.
|
||||
* @param a The first PCG state.
|
||||
* @param b The second PCG state.
|
||||
* @return The product of the PCG states.
|
||||
*/
|
||||
#define y_rand_mul2(a, b) (y_rand_multiply2((a), (b)))
|
||||
|
||||
/**
|
||||
* Add two PCG states.
|
||||
* @param a The first PCG state.
|
||||
* @param b The second PCG state.
|
||||
* @return The sum of the PCG states.
|
||||
*/
|
||||
#define y_rand_add2(a, b) (y_rand_addition2((a), (b)))
|
||||
|
||||
/**
|
||||
* Figure out if the PRNG has been modified. Since the increment of the PRNG has
|
||||
* to be odd, we use the extra bit to store whether it has been modified or not.
|
||||
* @param r The PRNG.
|
||||
* @return True if the PRNG has *not* been modified, false otherwise.
|
||||
*/
|
||||
#define y_RAND_NOTMODIFIED(r) (((r)->inc.lo & 1) == 0)
|
||||
|
||||
/**
|
||||
* Return true if the PRNG has not been seeded yet.
|
||||
* @param r The PRNG.
|
||||
* @return True if the PRNG has not been seeded yet, false otherwise.
|
||||
*/
|
||||
#define y_RAND_ZERO(r) (!(r)->state.lo && !(r)->state.hi)
|
||||
|
||||
/**
|
||||
* Returns a constant built from @a h and @a l.
|
||||
* @param h The high 64 bits.
|
||||
* @param l The low 64 bits.
|
||||
* @return The constant built from @a h and @a l.
|
||||
*/
|
||||
#define y_RAND_CONSTANT(h, l) \
|
||||
{ \
|
||||
.lo = (l), .hi = (h) \
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates a PCG state to the number of bits in a random integer.
|
||||
* @param s The state to truncate.
|
||||
* @return The truncated state.
|
||||
*/
|
||||
#define y_RAND_TRUNC(s) ((s).lo)
|
||||
|
||||
/**
|
||||
* Chops a PCG state in half and returns the top bits.
|
||||
* @param s The state to chop.
|
||||
* @return The chopped state's top bits.
|
||||
*/
|
||||
#define y_RAND_CHOP(s) ((s).hi)
|
||||
|
||||
/**
|
||||
* Returns the rotate amount for a PCG state.
|
||||
* @param s The state to rotate.
|
||||
* @return The semi-rotated state.
|
||||
*/
|
||||
#define y_RAND_ROTAMT(s) ((unsigned int) ((s).hi >> 58UL))
|
||||
|
||||
/**
|
||||
* @def y_RAND_BOTTOM32
|
||||
* A 64-bit integer with the bottom 32 bits set.
|
||||
*/
|
||||
#define y_RAND_BOTTOM32 y_BOTTOM32
|
||||
|
||||
/**
|
||||
* Returns the 32-bit truncated value of @a n.
|
||||
* @param n The integer to truncate.
|
||||
* @return The bottom 32 bits of @a n.
|
||||
*/
|
||||
#define y_RAND_TRUNC32(n) y_TRUNC32(n)
|
||||
|
||||
/**
|
||||
* Returns the second 32 bits of @a n.
|
||||
* @param n The integer to truncate.
|
||||
* @return The second 32 bits of @a n.
|
||||
*/
|
||||
#define y_RAND_CHOP32(n) y_CHOP32(n)
|
||||
|
||||
/**
|
||||
* @def y_RAND_INC
|
||||
* Sets an increment.
|
||||
* @param r The PRNG data.
|
||||
* @param i The increment to set.
|
||||
*/
|
||||
#define y_RAND_INC(r, i) \
|
||||
do \
|
||||
{ \
|
||||
i.lo = r->inc.lo | 1; \
|
||||
i.hi = r->inc.hi; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SETUPINC
|
||||
* Sets up the increment value.
|
||||
* @param r The PRNG data.
|
||||
*/
|
||||
#define y_RAND_SETUPINC(r) \
|
||||
do \
|
||||
{ \
|
||||
(r)->inc.hi <<= 1UL; \
|
||||
(r)->inc.hi |= ((r)->inc.lo & (1UL << (y_RAND_ROTC))) >> \
|
||||
(y_RAND_ROTC); \
|
||||
(r)->inc.lo <<= 1UL; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SEEDSTATE
|
||||
* Seeds the given state with the values.
|
||||
* @param s The state to seed.
|
||||
* @param v1 The first value.
|
||||
* @param v2 The second value.
|
||||
*/
|
||||
#define y_RAND_SEEDSTATE(s, v1, v2) \
|
||||
do \
|
||||
{ \
|
||||
(s)->lo = val1; \
|
||||
(s)->hi = val2; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* Gets the increment value from the PRNG state.
|
||||
* @param r The PRNG state.
|
||||
* @param i The variable to set to the increment.
|
||||
*/
|
||||
#define y_RAND_GETINC(r, i) \
|
||||
do \
|
||||
{ \
|
||||
i = (r)->inc; \
|
||||
i.lo >>= 1; \
|
||||
i.lo |= (i.hi & 1) << (y_RAND_ROTC); \
|
||||
i.hi >>= 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#endif // YC_128_STRUCT
|
||||
|
||||
/**
|
||||
* @def y_RAND_MULTIPLIER
|
||||
* A constant defined by PCG.
|
||||
*/
|
||||
#define y_RAND_MULTIPLIER \
|
||||
y_RAND_CONSTANT(2549297995355413924ULL, 4865540595714422341ULL)
|
||||
|
||||
/**
|
||||
* Returns the result of a PCG fold.
|
||||
* @param s The state to fold.
|
||||
* @return The folded state.
|
||||
*/
|
||||
#define y_RAND_FOLD(s) ((y_RandInt) (y_RAND_CHOP(s) ^ y_RAND_TRUNC(s)))
|
||||
|
||||
#else // YC_64BIT
|
||||
|
||||
/**
|
||||
* @def y_RAND_ROTC
|
||||
* A constant defined by PCG.
|
||||
*/
|
||||
#define y_RAND_ROTC (31)
|
||||
|
||||
/**
|
||||
* Multiply two integers, worrying about overflow.
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @return The product of the PCG states.
|
||||
*/
|
||||
#define y_rand_mul(a, b) (((y_RandState) (a)) * ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Add two integers, worrying about overflow.
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @return The sum of the PCG states.
|
||||
*/
|
||||
#define y_rand_add(a, b) (((y_RandState) (a)) + ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Multiply two PCG states.
|
||||
* @param a The first PCG state.
|
||||
* @param b The second PCG state.
|
||||
* @return The product of the PCG states.
|
||||
*/
|
||||
#define y_rand_mul2(a, b) (((y_RandState) (a)) * ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Add two PCG states.
|
||||
* @param a The first PCG state.
|
||||
* @param b The second PCG state.
|
||||
* @return The sum of the PCG states.
|
||||
*/
|
||||
#define y_rand_add2(a, b) (((y_RandState) (a)) + ((y_RandState) (b)))
|
||||
|
||||
/**
|
||||
* Figure out if the PRNG has been modified. Since the increment of the PRNG has
|
||||
* to be odd, we use the extra bit to store whether it has been modified or not.
|
||||
* @param r The PRNG.
|
||||
* @return True if the PRNG has *not* been modified, false otherwise.
|
||||
*/
|
||||
#define y_RAND_NOTMODIFIED(r) (((r)->inc & 1UL) == 0)
|
||||
|
||||
/**
|
||||
* Return true if the PRNG has not been seeded yet.
|
||||
* @param r The PRNG.
|
||||
* @return True if the PRNG has not been seeded yet, false otherwise.
|
||||
*/
|
||||
#define y_RAND_ZERO(r) (!(r)->state)
|
||||
|
||||
/**
|
||||
* Returns a constant built from a number.
|
||||
* @param n The number.
|
||||
* @return The constant built from @a n.
|
||||
*/
|
||||
#define y_RAND_CONSTANT(n) UINT64_C(n)
|
||||
|
||||
/**
|
||||
* @def y_RAND_MULTIPLIER
|
||||
* A constant defined by PCG.
|
||||
*/
|
||||
#define y_RAND_MULTIPLIER y_RAND_CONSTANT(6364136223846793005)
|
||||
|
||||
/**
|
||||
* Truncates a PCG state to the number of bits in a random integer.
|
||||
* @param s The state to truncate.
|
||||
* @return The truncated state.
|
||||
*/
|
||||
#define y_RAND_TRUNC(s) ((uint32_t) (s))
|
||||
|
||||
/**
|
||||
* Chops a PCG state in half and returns the top bits.
|
||||
* @param s The state to chop.
|
||||
* @return The chopped state's top bits.
|
||||
*/
|
||||
#define y_RAND_CHOP(s) ((uint32_t) ((s) >> 32UL))
|
||||
|
||||
/**
|
||||
* Returns the rotate amount for a PCG state.
|
||||
* @param s The state to rotate.
|
||||
* @return The semi-rotated state.
|
||||
*/
|
||||
#define y_RAND_ROTAMT(s) ((unsigned int) ((s) >> 59UL))
|
||||
|
||||
/**
|
||||
* Returns the result of a PCG fold.
|
||||
* @param s The state to fold.
|
||||
* @return The folded state.
|
||||
*/
|
||||
#define y_RAND_FOLD(s) ((y_RandInt) ((((s) >> 18U) ^ (s)) >> 27U))
|
||||
|
||||
/**
|
||||
* @def y_RAND_INC
|
||||
* Sets an increment.
|
||||
* @param r The PRNG data.
|
||||
* @param i The increment to set.
|
||||
*/
|
||||
#define y_RAND_INC(r, i) \
|
||||
do \
|
||||
{ \
|
||||
i = (r)->inc | 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SETUPINC
|
||||
* Sets up the increment value.
|
||||
* @param r The PRNG data.
|
||||
*/
|
||||
#define y_RAND_SETUPINC(r) \
|
||||
do \
|
||||
{ \
|
||||
(r)->inc <<= 1UL; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SEEDSTATE
|
||||
* Seeds the given state with the values.
|
||||
* @param s The state to seed.
|
||||
* @param v1 The first value.
|
||||
* @param v2 The second value.
|
||||
*/
|
||||
#define y_RAND_SEEDSTATE(s, v1, v2) \
|
||||
do \
|
||||
{ \
|
||||
*(s) = ((y_RandState) (v1)) | ((y_RandState) (v2)) << (32); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/**
|
||||
* Gets the increment value from the PRNG state.
|
||||
* @param r The PRNG state.
|
||||
* @param i The variable to set to the increment.
|
||||
*/
|
||||
#define y_RAND_GETINC(r, i) \
|
||||
do \
|
||||
{ \
|
||||
i = (r)->inc >> 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#endif // YC_64BIT
|
||||
|
||||
/**
|
||||
* Rotates @a v by @a r bits.
|
||||
* @param v The value to rotate.
|
||||
* @param r The amount to rotate by.
|
||||
* @return The rotated value.
|
||||
*/
|
||||
#define y_RAND_ROT(v, r) \
|
||||
((y_RandInt) (((v) >> (r)) | ((v) << ((0 - (r)) & y_RAND_ROTC))))
|
||||
|
||||
/**
|
||||
* @def y_RAND_BITS
|
||||
* The number of bits in a random integer.
|
||||
*/
|
||||
#define y_RAND_BITS (sizeof(y_RandInt) * CHAR_BIT)
|
||||
|
||||
/**
|
||||
* @def y_RAND_STATE_BITS
|
||||
* The number of bits in a PCG state.
|
||||
*/
|
||||
#define y_RAND_STATE_BITS (sizeof(y_RandState) * CHAR_BIT)
|
||||
|
||||
/**
|
||||
* @def y_RAND_SRAND_BITS
|
||||
* The mask for how many bits y_rand_srand() can set per iteration.
|
||||
*/
|
||||
#define y_RAND_SRAND_BITS ((1 << CHAR_BIT) - 1)
|
||||
|
||||
/**
|
||||
* Returns, via pointers, the state of the PRNG in pieces.
|
||||
* @param r The PRNG.
|
||||
* @param s1 The return value for the first part of the state.
|
||||
* @param s2 The return value for the second part of the state.
|
||||
* @param i1 The return value for the first part of the increment.
|
||||
* @param i2 The return value for |