/* ----> 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; | |
} | |