/**************************************************************************** * libs/libc/stdlib/lib_srand.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* First, second, and thired order congruential generators are supported */ #ifndef CONFIG_LIBC_RAND_ORDER # define CONFIG_LIBC_RAND_ORDER 1 #endif #if CONFIG_LIBC_RAND_ORDER > 3 # undef CONFIG_LIBC_RAND_ORDER # define CONFIG_LIBC_RAND_ORDER 3 #endif #ifndef CONFIG_HAVE_DOUBLE typedef float float_t; #else typedef double float_t; #endif /* Values needed by the random number generator */ #define RND1_CONSTK 470001 #define RND1_CONSTP 999563 #define RND2_CONSTK1 366528 #define RND2_CONSTK2 508531 #define RND2_CONSTP 998917 #define RND3_CONSTK1 360137 #define RND3_CONSTK2 519815 #define RND3_CONSTK3 616087 #define RND3_CONSTP 997783 /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* First order congruential generators */ static inline unsigned long fgenerate1(FAR unsigned long *seed); #if (CONFIG_LIBC_RAND_ORDER == 1) static float_t frand1(FAR unsigned long *seed); #endif /* Second order congruential generators */ #if (CONFIG_LIBC_RAND_ORDER > 1) static inline unsigned long fgenerate2(void); #if (CONFIG_LIBC_RAND_ORDER == 2) static float_t frand2(void); #endif /* Third order congruential generators */ #if (CONFIG_LIBC_RAND_ORDER > 2) static inline unsigned long fgenerate3(void); static float_t frand3(void); #endif #endif /**************************************************************************** * Private Data ****************************************************************************/ static unsigned long g_randint1; #if (CONFIG_LIBC_RAND_ORDER > 1) static unsigned long g_randint2; #if (CONFIG_LIBC_RAND_ORDER > 2) static unsigned long g_randint3; #endif #endif /**************************************************************************** * Private Functions ****************************************************************************/ /* First order congruential generators */ static inline unsigned long fgenerate1(FAR unsigned long *seed) { unsigned long randint; /* First order congruential generator. One may be added to the result of * the generated value to avoid the value zero. This would be fatal for * the first order random number generator. */ randint = (RND1_CONSTK * (*seed)) % RND1_CONSTP; *seed = (randint == 0 ? 1 : randint); return randint; } #if (CONFIG_LIBC_RAND_ORDER == 1) static float_t frand1(FAR unsigned long *seed) { /* First order congruential generator. */ unsigned long randint = fgenerate1(seed); /* Construct an floating point value in the range from 0.0 up to 1.0 */ return ((float_t)randint) / ((float_t)RND1_CONSTP); } #endif /* Second order congruential generators */ #if (CONFIG_LIBC_RAND_ORDER > 1) static inline unsigned long fgenerate2(void) { unsigned long randint; /* Second order congruential generator. */ randint = (RND2_CONSTK1 * g_randint1 + RND2_CONSTK2 * g_randint2) % RND2_CONSTP; g_randint2 = g_randint1; g_randint1 = randint; /* We cannot permit both values to become zero. That would be fatal for * the second order random number generator. */ if (g_randint2 == 0 && g_randint1 == 0) { g_randint2 = 1; } return randint; } #if (CONFIG_LIBC_RAND_ORDER == 2) static float_t frand2(void) { /* Second order congruential generator */ unsigned long randint = fgenerate2(); /* Construct an floating point value in the range from 0.0 up to 1.0 */ return ((float_t)randint) / ((float_t)RND2_CONSTP); } #endif /* Third order congruential generators */ #if (CONFIG_LIBC_RAND_ORDER > 2) static inline unsigned long fgenerate3(void) { unsigned long randint; /* Third order congruential generator. */ randint = (RND3_CONSTK1 * g_randint1 + RND3_CONSTK2 * g_randint2 + RND3_CONSTK2 * g_randint3) % RND3_CONSTP; g_randint3 = g_randint2; g_randint2 = g_randint1; g_randint1 = randint; /* We cannot permit all three values to become zero. That would be fatal * for the third order random number generator. */ if (g_randint3 == 0 && g_randint2 == 0 && g_randint1 == 0) { g_randint3 = 1; } return randint; } static float_t frand3(void) { /* Third order congruential generator */ unsigned long randint = fgenerate3(); /* Construct an floating point value in the range from 0.0 up to 1.0 */ return ((float_t)randint) / ((float_t)RND3_CONSTP); } #endif #endif static unsigned long nrand_r(unsigned long limit, FAR unsigned long *seed) { unsigned long result; float_t ratio; /* Loop to be sure a legal random number is generated */ do { /* Get a random integer in the range 0.0 - 1.0 */ #if (CONFIG_LIBC_RAND_ORDER == 1) ratio = frand1(seed); #elif (CONFIG_LIBC_RAND_ORDER == 2) ratio = frand2(); #else /* if (CONFIG_LIBC_RAND_ORDER > 2) */ ratio = frand3(); #endif /* Then, produce the return-able value in the requested range */ result = (unsigned long)(((float_t)limit) * ratio); /* Loop because there is an (unlikely) possibility that rounding * could increase the result at the limit value about the limit. */ } while (result >= limit); return result; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: srand * * Description: * Seed the congruential random number generator. * ****************************************************************************/ void srand(unsigned int seed) { g_randint1 = seed; #if (CONFIG_LIBC_RAND_ORDER > 1) g_randint2 = seed; fgenerate1(&g_randint1); #if (CONFIG_LIBC_RAND_ORDER > 2) g_randint3 = seed; fgenerate2(); #endif #endif } /**************************************************************************** * Name: nrand * * Description: * Return a random, unsigned long value in the range of 0 to (limit - 1) * ****************************************************************************/ unsigned long nrand(unsigned long limit) { return nrand_r(limit, &g_randint1); } /**************************************************************************** * Name: rand_r * * Description: * The function rand() is not reentrant, since it uses hidden state that * is modified on each call. This might just be the seed value to be used * by the next call, or it might be something more elaborate. In order to * get reproducible behavior in a threaded application, this state must be * made explicit; this can be done using the reentrant function rand_r(). * * Return a random, int value in the range of 0 to INT_MAX. * ****************************************************************************/ int rand_r(FAR unsigned int *seedp) { unsigned long seed = *seedp; unsigned long rand; rand = nrand_r(INT_MAX, &seed); *seedp = (unsigned int)seed; return (int)rand; }