FreeBASIC  0.91.0
math_rnd.c
Go to the documentation of this file.
1 /* rnd# function */
2 
3 #include "fb.h"
4 
5 #if defined HOST_WIN32
6  #include <windows.h>
7  #include <wincrypt.h>
8 #elif defined HOST_LINUX
9  #include <fcntl.h>
10 #endif
11 
12 #define RND_AUTO 0
13 #define RND_CRT 1
14 #define RND_FAST 2
15 #define RND_MTWIST 3
16 #define RND_QB 4
17 #define RND_REAL 5
18 
19 #define INITIAL_SEED 327680
20 
21 #define MAX_STATE 624
22 #define PERIOD 397
23 
24 static double hRnd_Startup( float n );
25 static double hRnd_CRT ( float n );
26 static double hRnd_QB ( float n );
27 static double ( *rnd_func )( float ) = hRnd_Startup;
28 static uint32_t iseed = INITIAL_SEED;
29 static uint32_t state[MAX_STATE], *p = NULL;
30 static double last_num = 0.0;
31 
32 static double hRnd_Startup( float n )
33 {
34  switch( __fb_ctx.lang ) {
35  case FB_LANG_QB:
36  rnd_func = hRnd_QB;
38  break;
39  case FB_LANG_FB_FBLITE:
42  break;
43  default:
44  fb_Randomize( 0.0, 0 );
45  break;
46  }
47  return fb_Rnd( n );
48 }
49 
50 static double hRnd_CRT ( float n )
51 {
52  if( n == 0.0 )
53  return last_num;
54 
55  /* return between 0 and 1 (but never 1) */
56  return (double)rand( ) * ( 1.0 / ( (double)RAND_MAX + 1.0 ) );
57 }
58 
59 static double hRnd_FAST ( float n )
60 {
61  /* return between 0 and 1 (but never 1) */
62  /* Constants from 'Numerical recipes in C' chapter 7.1 */
63  if( n != 0.0 )
64  iseed = ( ( 1664525 * iseed ) + 1013904223 );
65 
66  return (double)iseed / (double)4294967296ULL;
67 }
68 
69 static double hRnd_MTWIST ( float n )
70 {
71  if( n == 0.0 )
72  return last_num;
73 
74  uint32_t i, v, xor_mask[2] = { 0, 0x9908B0DF };
75 
76  if( !p ) {
77  /* initialize state starting with an initial seed */
79  }
80 
81  if( p >= state + MAX_STATE ) {
82  /* generate another array of 624 numbers */
83  for( i = 0; i < MAX_STATE - PERIOD; i++ ) {
84  v = ( state[i] & 0x80000000 ) | ( state[i + 1] & 0x7FFFFFFF );
85  state[i] = state[i + PERIOD] ^ ( v >> 1 ) ^ xor_mask[v & 0x1];
86  }
87  for( ; i < MAX_STATE - 1; i++ ) {
88  v = ( state[i] & 0x80000000 ) | ( state[i + 1] & 0x7FFFFFFF );
89  state[i] = state[i + PERIOD - MAX_STATE] ^ ( v >> 1 ) ^ xor_mask[v & 0x1];
90  }
91  v = ( state[MAX_STATE - 1] & 0x80000000 ) | ( state[0] & 0x7FFFFFFF );
92  state[MAX_STATE - 1] = state[PERIOD - 1] ^ ( v >> 1 ) ^ xor_mask[v & 0x1];
93  p = state;
94  }
95 
96  v = *p++;
97  v ^= ( v >> 11 );
98  v ^= ( ( v << 7 ) & 0x9D2C5680 );
99  v ^= ( ( v << 15 ) & 0xEFC60000 );
100  v ^= ( v >> 18 );
101 
102  return (double)v / (double)4294967296ULL;
103 }
104 
105 static double hRnd_QB ( float n )
106 {
107  union {
108  float f;
109  uint32_t i;
110  } ftoi;
111 
112  if( n != 0.0 ) {
113  if( n < 0.0 ) {
114  ftoi.f = n;
115  uint32_t s = ftoi.i;
116  iseed = s + ( s >> 24 );
117  }
118  iseed = ( ( iseed * 0xFD43FD ) + 0xC39EC3 ) & 0xFFFFFF;
119  }
120  return (float)iseed / (float)0x1000000;
121 }
122 
123 #if defined HOST_WIN32 || defined HOST_LINUX
124 static unsigned int hGetRealRndNumber( )
125 {
126  union {
127  unsigned int i;
128  unsigned char b[sizeof(unsigned int)];
129  } number = { 0 };
130 
131 #if defined HOST_WIN32
132  HCRYPTPROV provider = 0;
133  if( CryptAcquireContext( &provider, NULL, 0, PROV_RSA_FULL, 0 ) == TRUE ) {
134  if( CryptGenRandom( provider, sizeof(number), &number.b[0] ) == FALSE ) {
135  number.i = 0;
136  }
137  CryptReleaseContext( provider, 0 );
138  }
139 
140 #elif defined HOST_LINUX
141  int urandom = open( "/dev/urandom", O_RDONLY );
142  if( urandom != -1 ) {
143  if( read( urandom, &number.b[0], sizeof(number) ) != sizeof(number) ) {
144  number.i = 0;
145  }
146  close( urandom );
147  }
148 #endif
149 
150  return number.i;
151 }
152 
153 static double hRnd_REAL( float n )
154 {
155  static unsigned int count = 0;
156  static unsigned int v;
157  double mtwist;
158 
159  mtwist = hRnd_MTWIST(n);
160  if( (count % 256) == 0 ) {
161  count = 1;
162 
163  /* get new random number */
164  v = hGetRealRndNumber( );
165  } else {
166  count++;
167  }
168 
169  if( v == 0 ) {
170  return mtwist;
171  }
172  v *= mtwist;
173 
174  v ^= (v >> 11);
175  v ^= ((v << 7) & 0x9D2C5680);
176  v ^= ((v << 15) & 0xEFC60000);
177  v ^= (v >> 18);
178 
179  return (double)v / (double)4294967296ULL;
180 }
181 #endif
182 
183 FBCALL double fb_Rnd ( float n )
184 {
185  last_num = rnd_func( n );
186  return last_num;
187 }
188 
189 FBCALL void fb_Randomize ( double seed, int algorithm )
190 {
191  int i;
192 
193  union {
194  double d;
195  uint32_t i[2];
196  } dtoi;
197 
198  if( algorithm == RND_AUTO ) {
199  switch( __fb_ctx.lang ) {
200  case FB_LANG_QB: algorithm = RND_QB; break;
201  case FB_LANG_FB_FBLITE:
202  case FB_LANG_FB_DEPRECATED: algorithm = RND_CRT; break;
203  default:
204  case FB_LANG_FB: algorithm = RND_MTWIST; break;
205  }
206  }
207 
208  if( seed == -1.0 )
209  {
210  /* Take value of Timer to ensure a non-constant seed. The seeding
211  algorithms (with the exception of the QB one) take the integer value
212  of the seed, so make a value that will change more than once a second */
213 
214  dtoi.d = fb_Timer( );
215  seed = (double)(dtoi.i[0] ^ dtoi.i[1]);
216  }
217 
218  switch( algorithm ) {
219  case RND_CRT:
220  rnd_func = hRnd_CRT;
221  srand( (unsigned int)seed );
222  rand( );
223  break;
224 
225  case RND_FAST:
227  iseed = (uint32_t)seed;
228  break;
229 
230  case RND_QB:
231  rnd_func = hRnd_QB;
232  dtoi.d = seed;
233  uint32_t s = dtoi.i[1];
234  s ^= ( s >> 16 );
235  s = ( ( s & 0xFFFF ) << 8 ) | ( iseed & 0xFF );
236  iseed = s;
237  break;
238 
239 #if defined HOST_WIN32 || defined HOST_LINUX
240  case RND_REAL:
241  rnd_func = hRnd_REAL;
242  state[0] = (unsigned int)seed;
243  for( i = 1; i < MAX_STATE; i++ )
244  state[i] = ( state[i - 1] * 1664525 ) + 1013904223;
245  p = state + MAX_STATE;
246  break;
247 #endif
248 
249  default:
250  case RND_MTWIST:
252  state[0] = (unsigned int)seed;
253  for( i = 1; i < MAX_STATE; i++ )
254  state[i] = ( state[i - 1] * 1664525 ) + 1013904223;
255  p = state + MAX_STATE;
256  break;
257  }
258 }