Add the PCG PRNG as my seeded PRNG

This was actually a retooling of the one from my bc.

Signed-off-by: Gavin Howard <gavin@yzena.com>
master
Gavin D. Howard 2 months ago
parent f7208ff080
commit dd1dc708b2
Signed by: gavin
GPG Key ID: F890265DD80E4E90

@ -101,3 +101,29 @@ following license and copyright:
> 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.
## PCG
The files `include/yc/rand.h`, `src/util/rand.h`, and `src/util/rand.c` were
created from [PCG](https://github.com/imneme/pcg-c), which is under the
following license and copyright:
> 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.

@ -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

@ -51,6 +51,7 @@
set(YC_UTIL_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/rand.c"
"${CMAKE_CURRENT_SOURCE_DIR}/hash.c"
"${CMAKE_CURRENT_SOURCE_DIR}/opt.c"
"${CMAKE_CURRENT_SOURCE_DIR}/utf8.c"

@ -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