160 lines
5.0 KiB
C
160 lines
5.0 KiB
C
|
/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
|
||
|
|
||
|
Copyright (c) 2014-2015 Datalight, Inc.
|
||
|
All Rights Reserved Worldwide.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; use version 2 of the License.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
|
||
|
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along
|
||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*/
|
||
|
/* Businesses and individuals that for commercial or other reasons cannot
|
||
|
comply with the terms of the GPLv2 license may obtain a commercial license
|
||
|
before incorporating Reliance Edge into proprietary software for
|
||
|
distribution in any form. Visit http://www.datalight.com/reliance-edge for
|
||
|
more information.
|
||
|
*/
|
||
|
/** @file
|
||
|
@brief Implements a random number generator.
|
||
|
*/
|
||
|
#include <redfs.h>
|
||
|
#include <redtestutils.h>
|
||
|
|
||
|
|
||
|
/* This is the global seed used by the random number generator when the caller
|
||
|
has not provided a seed to either the RedRand32() or RedRand64() functions.
|
||
|
*/
|
||
|
static uint64_t ullGlobalRandomNumberSeed;
|
||
|
|
||
|
/* Whether the above seed has been initialized.
|
||
|
*/
|
||
|
static bool fGlobalSeedInited;
|
||
|
|
||
|
|
||
|
/** @brief Set the global seed used by the random number generator.
|
||
|
|
||
|
The global seed gets used when RedRand64() or RedRand32() are called with
|
||
|
a NULL seed argument.
|
||
|
|
||
|
@param ullSeed The value to use as the global RNG seed.
|
||
|
*/
|
||
|
void RedRandSeed(
|
||
|
uint64_t ullSeed)
|
||
|
{
|
||
|
ullGlobalRandomNumberSeed = ullSeed;
|
||
|
fGlobalSeedInited = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** @brief Generate a 64-bit pseudo-random number.
|
||
|
|
||
|
The period of this random number generator is 2^64 (1.8 x 1019). These
|
||
|
parameters are the same as the default one-stream SPRNG lcg64 generator and
|
||
|
it satisfies the requirements for a maximal period.
|
||
|
|
||
|
The tempering value is used and an AND mask and is specifically selected to
|
||
|
favor the distribution of lower bits.
|
||
|
|
||
|
@param pullSeed A pointer to the seed to use. Set this value to NULL to
|
||
|
use the internal global seed value.
|
||
|
|
||
|
@return A pseudo-random number in the range [0, UINT64_MAX].
|
||
|
*/
|
||
|
uint64_t RedRand64(
|
||
|
uint64_t *pullSeed)
|
||
|
{
|
||
|
const uint64_t ullA = UINT64_SUFFIX(2862933555777941757);
|
||
|
const uint64_t ullC = UINT64_SUFFIX(3037000493);
|
||
|
const uint64_t ullT = UINT64_SUFFIX(4921441182957829599);
|
||
|
uint64_t ullN;
|
||
|
uint64_t *pullSeedPtr;
|
||
|
uint64_t ullLocalSeed;
|
||
|
|
||
|
if(pullSeed != NULL)
|
||
|
{
|
||
|
ullLocalSeed = *pullSeed;
|
||
|
pullSeedPtr = pullSeed;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(!fGlobalSeedInited)
|
||
|
{
|
||
|
/* Unfortunately, the Reliance Edge OS services don't give us much
|
||
|
to work with to initialize the global seed. There is no entropy
|
||
|
abstraction, no tick count abstraction, and the timestamp
|
||
|
abstraction uses an opaque type which is not guaranteed to be an
|
||
|
integer. The best we can do is use the RTC.
|
||
|
|
||
|
Tests using the RNG should be supplying a seed anyway, for
|
||
|
reproducibility.
|
||
|
*/
|
||
|
RedRandSeed((uint64_t)RedOsClockGetTime());
|
||
|
}
|
||
|
|
||
|
ullLocalSeed = ullGlobalRandomNumberSeed;
|
||
|
pullSeedPtr = &ullGlobalRandomNumberSeed;
|
||
|
}
|
||
|
|
||
|
ullN = (ullLocalSeed * ullA) + ullC;
|
||
|
|
||
|
*pullSeedPtr = ullN;
|
||
|
|
||
|
/* The linear congruential generator used above produces good psuedo-random
|
||
|
64-bit number sequences, however, as with any LCG, the period of the
|
||
|
lower order bits is much shorter resulting in alternately odd/even pairs
|
||
|
in bit zero.
|
||
|
|
||
|
The result of the LGC above is tempered below with a series of XOR and
|
||
|
shift operations to produce a more acceptable equidistribution of bits
|
||
|
throughout the 64-bit range.
|
||
|
*/
|
||
|
ullN ^= (ullN >> 21U) & ullT;
|
||
|
ullN ^= (ullN >> 43U) & ullT;
|
||
|
ullN ^= (ullN << 23U) & ~ullT;
|
||
|
ullN ^= (ullN << 31U) & ~ullT;
|
||
|
|
||
|
return ullN;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** @brief Generate a 32-bit pseudo-random number.
|
||
|
|
||
|
@note The 32-bit random number generator internally uses the 64-bit random
|
||
|
number generator, returning the low 32-bits of the pseudo-random
|
||
|
64-bit value.
|
||
|
|
||
|
@param pulSeed A pointer to the seed to use. Set this value to NULL to use
|
||
|
the internal global seed value.
|
||
|
|
||
|
@return A pseudo-random number in the range [0, UINT32_MAX].
|
||
|
*/
|
||
|
uint32_t RedRand32(
|
||
|
uint32_t *pulSeed)
|
||
|
{
|
||
|
uint64_t ullN;
|
||
|
|
||
|
if(pulSeed != NULL)
|
||
|
{
|
||
|
uint64_t ullLocalSeed;
|
||
|
|
||
|
ullLocalSeed = *pulSeed;
|
||
|
ullN = RedRand64(&ullLocalSeed);
|
||
|
*pulSeed = (uint32_t)ullLocalSeed;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ullN = RedRand64(NULL);
|
||
|
}
|
||
|
|
||
|
return (uint32_t)ullN;
|
||
|
}
|
||
|
|