You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FreeRTOS/FreeRTOS-Plus/Source/Reliance-Edge/tests/util/rand.c

158 lines
5.1 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 pseudo-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;
}