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