source: trunk/third/glib2/glib/grand.c @ 18159

Revision 18159, 12.1 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18158, which included commits to RCS files with non-trunk default branches.
Line 
1/* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20/* Originally developed and coded by Makoto Matsumoto and Takuji
21 * Nishimura.  Please mail <matumoto@math.keio.ac.jp>, if you're using
22 * code from this file in your own programs or libraries.
23 * Further information on the Mersenne Twister can be found at
24 * http://www.math.keio.ac.jp/~matumoto/emt.html
25 * This code was adapted to glib by Sebastian Wilhelmi <wilhelmi@ira.uka.de>.
26 */
27
28/*
29 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
30 * file for a list of people on the GLib Team.  See the ChangeLog
31 * files for a list of changes.  These files are distributed with
32 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
33 */
34
35/*
36 * MT safe
37 */
38
39#include "config.h"
40
41#include <math.h>
42#include <stdio.h>
43#include <string.h>
44
45#include "glib.h"
46
47
48G_LOCK_DEFINE_STATIC (global_random);
49static GRand* global_random = NULL;
50
51/* Period parameters */ 
52#define N 624
53#define M 397
54#define MATRIX_A 0x9908b0df   /* constant vector a */
55#define UPPER_MASK 0x80000000 /* most significant w-r bits */
56#define LOWER_MASK 0x7fffffff /* least significant r bits */
57
58/* Tempering parameters */   
59#define TEMPERING_MASK_B 0x9d2c5680
60#define TEMPERING_MASK_C 0xefc60000
61#define TEMPERING_SHIFT_U(y)  (y >> 11)
62#define TEMPERING_SHIFT_S(y)  (y << 7)
63#define TEMPERING_SHIFT_T(y)  (y << 15)
64#define TEMPERING_SHIFT_L(y)  (y >> 18)
65
66static guint
67get_random_version (void)
68{
69  static gboolean initialized = FALSE;
70  static guint random_version;
71 
72  if (!initialized)
73    {
74      const gchar *version_string = g_getenv ("G_RANDOM_VERSION");
75      if (!version_string || version_string[0] == '\000' ||
76          strcmp (version_string, "2.2") == 0)
77        random_version = 22;
78      else if (strcmp (version_string, "2.0") == 0)
79        random_version = 20;
80      else
81        {
82          g_warning ("Unknown G_RANDOM_VERSION \"%s\". Using version 2.2.",
83                     version_string);
84          random_version = 22;
85        }
86      initialized = TRUE;
87    }
88 
89  return random_version;
90}
91
92/* This is called from g_thread_init(). It's used to
93 * initialize some static data in a threadsafe way.
94 */
95void
96g_rand_init (void)
97{
98  (void)get_random_version ();
99}
100
101struct _GRand
102{
103  guint32 mt[N]; /* the array for the state vector  */
104  guint mti;
105};
106
107/**
108 * g_rand_new_with_seed:
109 * @seed: a value to initialize the random number generator.
110 *
111 * Creates a new random number generator initialized with @seed.
112 *
113 * Return value: the new #GRand.
114 **/
115GRand*
116g_rand_new_with_seed (guint32 seed)
117{
118  GRand *rand = g_new0 (GRand, 1);
119  g_rand_set_seed (rand, seed);
120  return rand;
121}
122
123/**
124 * g_rand_new:
125 *
126 * Creates a new random number generator initialized with a seed taken
127 * either from <filename>/dev/urandom</filename> (if existing) or from
128 * the current time (as a fallback).
129 *
130 * Return value: the new #GRand.
131 **/
132GRand*
133g_rand_new (void)
134{
135  guint32 seed;
136  GTimeVal now;
137#ifdef G_OS_UNIX
138  static gboolean dev_urandom_exists = TRUE;
139
140  if (dev_urandom_exists)
141    {
142      FILE* dev_urandom = fopen("/dev/urandom", "rb");
143      if (dev_urandom)
144        {
145          if (fread (&seed, sizeof (seed), 1, dev_urandom) != 1)
146            dev_urandom_exists = FALSE;
147          fclose (dev_urandom);
148        }       
149      else
150        dev_urandom_exists = FALSE;
151    }
152#else
153  static gboolean dev_urandom_exists = FALSE;
154#endif
155
156  if (!dev_urandom_exists)
157    { 
158      g_get_current_time (&now);
159      seed = now.tv_sec ^ now.tv_usec;
160    }
161
162  return g_rand_new_with_seed (seed);
163}
164
165/**
166 * g_rand_free:
167 * @rand_: a #GRand.
168 *
169 * Frees the memory allocated for the #GRand.
170 **/
171void
172g_rand_free (GRand* rand)
173{
174  g_return_if_fail (rand != NULL);
175
176  g_free (rand);
177}
178
179/**
180 * g_rand_set_seed:
181 * @rand_: a #GRand.
182 * @seed: a value to reinitialize the random number generator.
183 *
184 * Sets the seed for the random number generator #GRand to @seed.
185 **/
186void
187g_rand_set_seed (GRand* rand, guint32 seed)
188{
189  g_return_if_fail (rand != NULL);
190
191  switch (get_random_version ())
192    {
193    case 20:
194      /* setting initial seeds to mt[N] using         */
195      /* the generator Line 25 of Table 1 in          */
196      /* [KNUTH 1981, The Art of Computer Programming */
197      /*    Vol. 2 (2nd Ed.), pp102]                  */
198     
199      if (seed == 0) /* This would make the PRNG procude only zeros */
200        seed = 0x6b842128; /* Just set it to another number */
201     
202      rand->mt[0]= seed;
203      for (rand->mti=1; rand->mti<N; rand->mti++)
204        rand->mt[rand->mti] = (69069 * rand->mt[rand->mti-1]);
205     
206      break;
207    case 22:
208      /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
209      /* In the previous version (see above), MSBs of the    */
210      /* seed affect only MSBs of the array mt[].            */
211     
212      rand->mt[0]= seed;
213      for (rand->mti=1; rand->mti<N; rand->mti++)
214        rand->mt[rand->mti] = 1812433253UL *
215          (rand->mt[rand->mti-1] ^ (rand->mt[rand->mti-1] >> 30)) + rand->mti;
216      break;
217    default:
218      g_assert_not_reached ();
219    }
220}
221
222/**
223 * g_rand_int:
224 * @rand_: a #GRand.
225 *
226 * Returns the next random #guint32 from @rand_ equally distributed over
227 * the range [0..2^32-1].
228 *
229 * Return value: A random number.
230 **/
231guint32
232g_rand_int (GRand* rand)
233{
234  guint32 y;
235  static const guint32 mag01[2]={0x0, MATRIX_A};
236  /* mag01[x] = x * MATRIX_A  for x=0,1 */
237
238  g_return_val_if_fail (rand != NULL, 0);
239
240  if (rand->mti >= N) { /* generate N words at one time */
241    int kk;
242   
243    for (kk=0;kk<N-M;kk++) {
244      y = (rand->mt[kk]&UPPER_MASK)|(rand->mt[kk+1]&LOWER_MASK);
245      rand->mt[kk] = rand->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
246    }
247    for (;kk<N-1;kk++) {
248      y = (rand->mt[kk]&UPPER_MASK)|(rand->mt[kk+1]&LOWER_MASK);
249      rand->mt[kk] = rand->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
250    }
251    y = (rand->mt[N-1]&UPPER_MASK)|(rand->mt[0]&LOWER_MASK);
252    rand->mt[N-1] = rand->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
253   
254    rand->mti = 0;
255  }
256 
257  y = rand->mt[rand->mti++];
258  y ^= TEMPERING_SHIFT_U(y);
259  y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
260  y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
261  y ^= TEMPERING_SHIFT_L(y);
262 
263  return y;
264}
265
266/* transform [0..2^32] -> [0..1] */
267#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10
268
269/**
270 * g_rand_int_range:
271 * @rand_: a #GRand.
272 * @begin: lower closed bound of the interval.
273 * @end: upper open bound of the interval.
274 *
275 * Returns the next random #gint32 from @rand_ equally distributed over
276 * the range [@begin..@end-1].
277 *
278 * Return value: A random number.
279 **/
280gint32
281g_rand_int_range (GRand* rand, gint32 begin, gint32 end)
282{
283  guint32 dist = end - begin;
284  guint32 random;
285
286  g_return_val_if_fail (rand != NULL, begin);
287  g_return_val_if_fail (end > begin, begin);
288
289  switch (get_random_version ())
290    {
291    case 20:
292      if (dist <= 0x10000L) /* 2^16 */
293        {
294          /* This method, which only calls g_rand_int once is only good
295           * for (end - begin) <= 2^16, because we only have 32 bits set
296           * from the one call to g_rand_int (). */
297         
298          /* we are using (trans + trans * trans), because g_rand_int only
299           * covers [0..2^32-1] and thus g_rand_int * trans only covers
300           * [0..1-2^-32], but the biggest double < 1 is 1-2^-52.
301           */
302         
303          gdouble double_rand = g_rand_int (rand) *
304            (G_RAND_DOUBLE_TRANSFORM +
305             G_RAND_DOUBLE_TRANSFORM * G_RAND_DOUBLE_TRANSFORM);
306         
307          random = (gint32) (double_rand * dist);
308        }
309      else
310        {
311          /* Now we use g_rand_double_range (), which will set 52 bits for
312             us, so that it is safe to round and still get a decent
313             distribution */
314          random = (gint32) g_rand_double_range (rand, 0, dist);
315        }
316      break;
317    case 22:
318      if (dist == 0)
319        random = 0;
320      else
321        {
322          /* maxvalue is set to the predecessor of the greatest
323           * multiple of dist less or equal 2^32. */
324          guint32 maxvalue;
325          if (dist <= 0x80000000u) /* 2^31 */
326            {
327              /* maxvalue = 2^32 - 1 - (2^32 % dist) */
328              guint32 leftover = (0x80000000u % dist) * 2;
329              if (leftover >= dist) leftover -= dist;
330              maxvalue = 0xffffffffu - leftover;
331            }
332          else
333            maxvalue = dist - 1;
334         
335          do
336            random = g_rand_int (rand);
337          while (random > maxvalue);
338         
339          random %= dist;
340        }
341      break;
342    default:
343      random = 0;               /* Quiet GCC */
344      g_assert_not_reached ();
345    }     
346 
347  return begin + random;
348}
349
350/**
351 * g_rand_double:
352 * @rand_: a #GRand.
353 *
354 * Returns the next random #gdouble from @rand_ equally distributed over
355 * the range [0..1).
356 *
357 * Return value: A random number.
358 **/
359gdouble
360g_rand_double (GRand* rand)
361{   
362  /* We set all 52 bits after the point for this, not only the first
363     32. Thats why we need two calls to g_rand_int */
364  gdouble retval = g_rand_int (rand) * G_RAND_DOUBLE_TRANSFORM;
365  retval = (retval + g_rand_int (rand)) * G_RAND_DOUBLE_TRANSFORM;
366
367  /* The following might happen due to very bad rounding luck, but
368   * actually this should be more than rare, we just try again then */
369  if (retval >= 1.0)
370    return g_rand_double (rand);
371
372  return retval;
373}
374
375/**
376 * g_rand_double_range:
377 * @rand_: a #GRand.
378 * @begin: lower closed bound of the interval.
379 * @end: upper open bound of the interval.
380 *
381 * Returns the next random #gdouble from @rand_ equally distributed over
382 * the range [@begin..@end).
383 *
384 * Return value: A random number.
385 **/
386gdouble
387g_rand_double_range (GRand* rand, gdouble begin, gdouble end)
388{
389  return g_rand_double (rand) * (end - begin) + begin;
390}
391
392/**
393 * g_random_int:
394 *
395 * Return a random #guint32 equally distributed over the range
396 * [0..2^32-1].
397 *
398 * Return value: A random number.
399 **/
400guint32
401g_random_int (void)
402{
403  guint32 result;
404  G_LOCK (global_random);
405  if (!global_random)
406    global_random = g_rand_new ();
407 
408  result = g_rand_int (global_random);
409  G_UNLOCK (global_random);
410  return result;
411}
412
413/**
414 * g_random_int_range:
415 * @begin: lower closed bound of the interval.
416 * @end: upper open bound of the interval.
417 *
418 * Returns a random #gint32 equally distributed over the range
419 * [@begin..@end-1].
420 *
421 * Return value: A random number.
422 **/
423gint32
424g_random_int_range (gint32 begin, gint32 end)
425{
426  gint32 result;
427  G_LOCK (global_random);
428  if (!global_random)
429    global_random = g_rand_new ();
430 
431  result = g_rand_int_range (global_random, begin, end);
432  G_UNLOCK (global_random);
433  return result;
434}
435
436/**
437 * g_random_double:
438 *
439 * Returns a random #gdouble equally distributed over the range [0..1).
440 *
441 * Return value: A random number.
442 **/
443gdouble
444g_random_double (void)
445{
446  double result;
447  G_LOCK (global_random);
448  if (!global_random)
449    global_random = g_rand_new ();
450 
451  result = g_rand_double (global_random);
452  G_UNLOCK (global_random);
453  return result;
454}
455
456/**
457 * g_random_double_range:
458 * @begin: lower closed bound of the interval.
459 * @end: upper open bound of the interval.
460 *
461 * Returns a random #gdouble equally distributed over the range [@begin..@end).
462 *
463 * Return value: A random number.
464 **/
465gdouble
466g_random_double_range (gdouble begin, gdouble end)
467{
468  double result;
469  G_LOCK (global_random);
470  if (!global_random)
471    global_random = g_rand_new ();
472 
473  result = g_rand_double_range (global_random, begin, end);
474  G_UNLOCK (global_random);
475  return result;
476}
477
478/**
479 * g_random_set_seed:
480 * @seed: a value to reinitialize the global random number generator.
481 *
482 * Sets the seed for the global random number generator, which is used
483 * by the <function>g_random_*</function> functions, to @seed.
484 **/
485void
486g_random_set_seed (guint32 seed)
487{
488  G_LOCK (global_random);
489  if (!global_random)
490    global_random = g_rand_new_with_seed (seed);
491  else
492    g_rand_set_seed (global_random, seed);
493  G_UNLOCK (global_random);
494}
495
Note: See TracBrowser for help on using the repository browser.