Removed empty file genio.h and references to it.
[bpt/guile.git] / libguile / random.c
1 /* Copyright (C) 1999, 2000 Free Software Foundation, Inc.
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2, or (at your option)
5 * any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this software; see the file COPYING. If not, write to
14 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15 * Boston, MA 02111-1307 USA
16 *
17 * As a special exception, the Free Software Foundation gives permission
18 * for additional uses of the text contained in its release of GUILE.
19 *
20 * The exception is that, if you link the GUILE library with other files
21 * to produce an executable, this does not by itself cause the
22 * resulting executable to be covered by the GNU General Public License.
23 * Your use of that executable is in no way restricted on account of
24 * linking the GUILE library code into it.
25 *
26 * This exception does not however invalidate any other reasons why
27 * the executable file might be covered by the GNU General Public License.
28 *
29 * This exception applies only to the code released by the
30 * Free Software Foundation under the name GUILE. If you copy
31 * code from other Free Software Foundation releases into a copy of
32 * GUILE, as the General Public License permits, the exception does
33 * not apply to the code that you add in this way. To avoid misleading
34 * anyone as to the status of such modified files, you must delete
35 * this exception notice from them.
36 *
37 * If you write modifications of your own for GUILE, it is your choice
38 * whether to permit this exception to apply to your modifications.
39 * If you do not wish that, delete this exception notice. */
40
41 /* Software engineering face-lift by Greg J. Badros, 11-Dec-1999,
42 gjb@cs.washington.edu, http://www.cs.washington.edu/homes/gjb */
43
44
45 /* Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
46
47 #include "_scm.h"
48
49 #include <stdio.h>
50 #include <math.h>
51 #include "smob.h"
52 #include "numbers.h"
53 #include "feature.h"
54
55 #include "validate.h"
56 #include "random.h"
57
58 \f
59 /*
60 * A plugin interface for RNGs
61 *
62 * Using this interface, it is possible for the application to tell
63 * libguile to use a different RNG. This is desirable if it is
64 * necessary to use the same RNG everywhere in the application in
65 * order to prevent interference, if the application uses RNG
66 * hardware, or if the application has special demands on the RNG.
67 *
68 * Look in random.h and how the default generator is "plugged in" in
69 * scm_init_random().
70 */
71
72 scm_rng scm_the_rng;
73
74 \f
75 /*
76 * The prepackaged RNG
77 *
78 * This is the MWC (Multiply With Carry) random number generator
79 * described by George Marsaglia at the Department of Statistics and
80 * Supercomputer Computations Research Institute, The Florida State
81 * University (http://stat.fsu.edu/~geo).
82 *
83 * It uses 64 bits, has a period of 4578426017172946943 (4.6e18), and
84 * passes all tests in the DIEHARD test suite
85 * (http://stat.fsu.edu/~geo/diehard.html)
86 */
87
88 #define A 2131995753UL
89
90 #if SIZEOF_LONG > 4
91 #if SIZEOF_INT > 4
92 #define LONG32 unsigned short
93 #else
94 #define LONG32 unsigned int
95 #endif
96 #define LONG64 unsigned long
97 #else
98 #define LONG32 unsigned long
99 #define LONG64 unsigned long long
100 #endif
101
102 #if SIZEOF_LONG > 4 || defined (HAVE_LONG_LONGS)
103
104 unsigned long
105 scm_i_uniform32 (scm_i_rstate *state)
106 {
107 LONG64 x = (LONG64) A * state->w + state->c;
108 LONG32 w = x & 0xffffffffUL;
109 state->w = w;
110 state->c = x >> 32L;
111 return w;
112 }
113
114 #else
115
116 /* ww This is a portable version of the same RNG without 64 bit
117 * * aa arithmetic.
118 * ----
119 * xx It is only intended to provide identical behaviour on
120 * xx platforms without 8 byte longs or long longs until
121 * xx someone has implemented the routine in assembler code.
122 * xxcc
123 * ----
124 * ccww
125 */
126
127 #define L(x) ((x) & 0xffff)
128 #define H(x) ((x) >> 16)
129
130 unsigned long
131 scm_i_uniform32 (scm_i_rstate *state)
132 {
133 LONG32 x1 = L (A) * L (state->w);
134 LONG32 x2 = L (A) * H (state->w);
135 LONG32 x3 = H (A) * L (state->w);
136 LONG32 w = L (x1) + L (state->c);
137 LONG32 m = H (x1) + L (x2) + L (x3) + H (state->c) + H (w);
138 LONG32 x4 = H (A) * H (state->w);
139 state->w = w = (L (m) << 16) + L (w);
140 state->c = H (x2) + H (x3) + x4 + H (m);
141 return w;
142 }
143
144 #endif
145
146 void
147 scm_i_init_rstate (scm_i_rstate *state, char *seed, int n)
148 {
149 LONG32 w = 0L;
150 LONG32 c = 0L;
151 int i, m;
152 for (i = 0; i < n; ++i)
153 {
154 m = i % 8;
155 if (m < 4)
156 w += seed[i] << (8 * m);
157 else
158 c += seed[i] << (8 * (m - 4));
159 }
160 if ((w == 0 && c == 0) || (w == 0xffffffffUL && c == A - 1))
161 ++c;
162 state->w = w;
163 state->c = c;
164 }
165
166 scm_i_rstate *
167 scm_i_copy_rstate (scm_i_rstate *state)
168 {
169 scm_rstate *new_state = malloc (scm_the_rng.rstate_size);
170 if (new_state == 0)
171 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
172 (char *) SCM_NALLOC, "rstate");
173 return memcpy (new_state, state, scm_the_rng.rstate_size);
174 }
175
176 \f
177 /*
178 * Random number library functions
179 */
180
181 scm_rstate *
182 scm_c_make_rstate (char *seed, int n)
183 {
184 scm_rstate *state = malloc (scm_the_rng.rstate_size);
185 if (state == 0)
186 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
187 (char *) SCM_NALLOC,
188 "rstate");
189 state->reserved0 = 0;
190 scm_the_rng.init_rstate (state, seed, n);
191 return state;
192 }
193
194 scm_rstate *
195 scm_c_default_rstate ()
196 {
197 SCM state = SCM_CDR (scm_var_random_state);
198 SCM_ASSERT (SCM_RSTATEP (state),
199 state, "*random-state* contains bogus random state", 0);
200 return SCM_RSTATE (state);
201 }
202
203 inline double
204 scm_c_uniform01 (scm_rstate *state)
205 {
206 double x = (double) scm_the_rng.random_bits (state) / (double) 0xffffffffUL;
207 return ((x + (double) scm_the_rng.random_bits (state))
208 / (double) 0xffffffffUL);
209 }
210
211 double
212 scm_c_normal01 (scm_rstate *state)
213 {
214 if (state->reserved0)
215 {
216 state->reserved0 = 0;
217 return state->reserved1;
218 }
219 else
220 {
221 double r, a, n;
222
223 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
224 a = 2.0 * M_PI * scm_c_uniform01 (state);
225
226 n = r * sin (a);
227 state->reserved1 = r * cos (a);
228 state->reserved0 = 1;
229
230 return n;
231 }
232 }
233
234 double
235 scm_c_exp1 (scm_rstate *state)
236 {
237 return - log (scm_c_uniform01 (state));
238 }
239
240 unsigned char scm_masktab[256];
241
242 unsigned long
243 scm_c_random (scm_rstate *state, unsigned long m)
244 {
245 unsigned int r, mask;
246 mask = (m < 0x100
247 ? scm_masktab[m]
248 : (m < 0x10000
249 ? scm_masktab[m >> 8] << 8 | 0xff
250 : (m < 0x1000000
251 ? scm_masktab[m >> 16] << 16 | 0xffff
252 : scm_masktab[m >> 24] << 24 | 0xffffff)));
253 while ((r = scm_the_rng.random_bits (state) & mask) >= m);
254 return r;
255 }
256
257 SCM
258 scm_c_random_bignum (scm_rstate *state, SCM m)
259 {
260 SCM b;
261 int i, nd;
262 LONG32 *bits, mask, w;
263 nd = SCM_NUMDIGS (m);
264 /* calculate mask for most significant digit */
265 #if SIZEOF_INT == 4
266 /* 16 bit digits */
267 if (nd & 1)
268 {
269 /* fix most significant 16 bits */
270 unsigned short s = SCM_BDIGITS (m)[nd - 1];
271 mask = s < 0x100 ? scm_masktab[s] : scm_masktab[s >> 8] << 8 | 0xff;
272 }
273 else
274 #endif
275 {
276 /* fix most significant 32 bits */
277 #if SIZEOF_INT == 4
278 w = SCM_BDIGITS (m)[nd - 1] << 16 | SCM_BDIGITS (m)[nd - 2];
279 #else
280 w = SCM_BDIGITS (m)[nd - 1];
281 #endif
282 mask = (w < 0x10000
283 ? (w < 0x100
284 ? scm_masktab[w]
285 : scm_masktab[w >> 8] << 8 | 0xff)
286 : (w < 0x1000000
287 ? scm_masktab[w >> 16] << 16 | 0xffff
288 : scm_masktab[w >> 24] << 24 | 0xffffff));
289 }
290 b = scm_mkbig (nd, 0);
291 bits = (LONG32 *) SCM_BDIGITS (b);
292 do
293 {
294 i = nd;
295 /* treat most significant digit specially */
296 #if SIZEOF_INT == 4
297 /* 16 bit digits */
298 if (i & 1)
299 {
300 ((SCM_BIGDIG*) bits)[i - 1] = scm_the_rng.random_bits (state) & mask;
301 i /= 2;
302 }
303 else
304 #endif
305 {
306 /* fix most significant 32 bits */
307 #if SIZEOF_INT == 4
308 w = scm_the_rng.random_bits (state) & mask;
309 ((SCM_BIGDIG*) bits)[i - 2] = w & 0xffff;
310 ((SCM_BIGDIG*) bits)[i - 1] = w >> 16;
311 i = i / 2 - 1;
312 #else
313 i /= 2;
314 bits[--i] = scm_the_rng.random_bits (state) & mask;
315 #endif
316 }
317 /* now fill up the rest of the bignum */
318 while (i)
319 bits[--i] = scm_the_rng.random_bits (state);
320 b = scm_normbig (b);
321 if (SCM_INUMP (b))
322 return b;
323 } while (scm_bigcomp (b, m) <= 0);
324 return b;
325 }
326
327 /*
328 * Scheme level representation of random states.
329 */
330
331 long scm_tc16_rstate;
332
333 static SCM
334 make_rstate (scm_rstate *state)
335 {
336 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
337 }
338
339 static scm_sizet
340 free_rstate (SCM rstate)
341 {
342 free (SCM_RSTATE (rstate));
343 return scm_the_rng.rstate_size;
344 }
345
346 /*
347 * Scheme level interface.
348 */
349
350 SCM_GLOBAL_VCELL_INIT (scm_var_random_state, "*random-state*", scm_seed_to_random_state (scm_makfrom0str ("URL:http://stat.fsu.edu/~geo/diehard.html")));
351
352 SCM_DEFINE (scm_random, "random", 1, 1, 0,
353 (SCM n, SCM state),
354 "Return a number in [0,N).\n"
355 "\n"
356 "Accepts a positive integer or real n and returns a \n"
357 "number of the same type between zero (inclusive) and \n"
358 "N (exclusive). The values returned have a uniform \n"
359 "distribution.\n"
360 "\n"
361 "The optional argument STATE must be of the type produced by\n"
362 "`seed->andom-state'. It defaults to the value of the variable\n"
363 "*random-state*. This object is used to maintain the state of\n"
364 "the pseudo-random-number generator and is altered as a side\n"
365 "effect of the random operation.\n"
366 "")
367 #define FUNC_NAME s_scm_random
368 {
369 if (SCM_UNBNDP (state))
370 state = SCM_CDR (scm_var_random_state);
371 SCM_VALIDATE_RSTATE (2,state);
372 if (SCM_INUMP (n))
373 {
374 unsigned long m = SCM_INUM (n);
375 SCM_ASSERT_RANGE (1,n,m > 0);
376 return SCM_MAKINUM (scm_c_random (SCM_RSTATE (state), m));
377 }
378 SCM_VALIDATE_NIM (1,n);
379 if (SCM_REALP (n))
380 return scm_makdbl (SCM_REALPART (n) * scm_c_uniform01 (SCM_RSTATE (state)),
381 0.0);
382 SCM_VALIDATE_SMOB (1, n, big);
383 return scm_c_random_bignum (SCM_RSTATE (state), n);
384 }
385 #undef FUNC_NAME
386
387 SCM_DEFINE (scm_copy_random_state, "copy-random-state", 0, 1, 0,
388 (SCM state),
389 "Return a copy of the random state STATE.")
390 #define FUNC_NAME s_scm_copy_random_state
391 {
392 if (SCM_UNBNDP (state))
393 state = SCM_CDR (scm_var_random_state);
394 SCM_VALIDATE_RSTATE (1,state);
395 return make_rstate (scm_the_rng.copy_rstate (SCM_RSTATE (state)));
396 }
397 #undef FUNC_NAME
398
399 SCM_DEFINE (scm_seed_to_random_state, "seed->random-state", 1, 0, 0,
400 (SCM seed),
401 "Return a new random state using SEED.")
402 #define FUNC_NAME s_scm_seed_to_random_state
403 {
404 if (SCM_NUMBERP (seed))
405 seed = scm_number_to_string (seed, SCM_UNDEFINED);
406 SCM_VALIDATE_STRING (1,seed);
407 return make_rstate (scm_c_make_rstate (SCM_ROCHARS (seed),
408 SCM_LENGTH (seed)));
409 }
410 #undef FUNC_NAME
411
412 SCM_DEFINE (scm_random_uniform, "random:uniform", 0, 1, 0,
413 (SCM state),
414 "Returns a uniformly distributed inexact real random number in [0,1).")
415 #define FUNC_NAME s_scm_random_uniform
416 {
417 if (SCM_UNBNDP (state))
418 state = SCM_CDR (scm_var_random_state);
419 SCM_VALIDATE_RSTATE (1,state);
420 return scm_makdbl (scm_c_uniform01 (SCM_RSTATE (state)), 0.0);
421 }
422 #undef FUNC_NAME
423
424 SCM_DEFINE (scm_random_normal, "random:normal", 0, 1, 0,
425 (SCM state),
426 "Returns an inexact real in a normal distribution.\n"
427 "The distribution used has mean 0 and standard deviation 1.\n"
428 "For a normal distribution with mean m and standard deviation\n"
429 "d use @code{(+ m (* d (random:normal)))}.\n"
430 "")
431 #define FUNC_NAME s_scm_random_normal
432 {
433 if (SCM_UNBNDP (state))
434 state = SCM_CDR (scm_var_random_state);
435 SCM_VALIDATE_RSTATE (1,state);
436 return scm_makdbl (scm_c_normal01 (SCM_RSTATE (state)), 0.0);
437 }
438 #undef FUNC_NAME
439
440 #ifdef HAVE_ARRAYS
441
442 static void
443 vector_scale (SCM v, double c)
444 {
445 int n = SCM_LENGTH (v);
446 if (SCM_VECTORP (v))
447 while (--n >= 0)
448 SCM_REAL (SCM_VELTS (v)[n]) *= c;
449 else
450 while (--n >= 0)
451 ((double *) SCM_VELTS (v))[n] *= c;
452 }
453
454 static double
455 vector_sum_squares (SCM v)
456 {
457 double x, sum = 0.0;
458 int n = SCM_LENGTH (v);
459 if (SCM_VECTORP (v))
460 while (--n >= 0)
461 {
462 x = SCM_REAL (SCM_VELTS (v)[n]);
463 sum += x * x;
464 }
465 else
466 while (--n >= 0)
467 {
468 x = ((double *) SCM_VELTS (v))[n];
469 sum += x * x;
470 }
471 return sum;
472 }
473
474 /* For the uniform distribution on the solid sphere, note that in
475 * this distribution the length r of the vector has cumulative
476 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
477 * generated as r=u^(1/n).
478 */
479 SCM_DEFINE (scm_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0,
480 (SCM v, SCM state),
481 "Fills vect with inexact real random numbers\n"
482 "the sum of whose squares is less than 1.0.\n"
483 "Thinking of vect as coordinates in space of \n"
484 "dimension n = (vector-length vect), the coordinates \n"
485 "are uniformly distributed within the unit n-shere.\n"
486 "The sum of the squares of the numbers is returned.\n"
487 "")
488 #define FUNC_NAME s_scm_random_solid_sphere_x
489 {
490 SCM_VALIDATE_VECTOR_OR_DVECTOR (1,v);
491 if (SCM_UNBNDP (state))
492 state = SCM_CDR (scm_var_random_state);
493 SCM_VALIDATE_RSTATE (2,state);
494 scm_random_normal_vector_x (v, state);
495 vector_scale (v,
496 pow (scm_c_uniform01 (SCM_RSTATE (state)),
497 1.0 / SCM_LENGTH (v))
498 / sqrt (vector_sum_squares (v)));
499 return SCM_UNSPECIFIED;
500 }
501 #undef FUNC_NAME
502
503 SCM_DEFINE (scm_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0,
504 (SCM v, SCM state),
505 "Fills vect with inexact real random numbers\n"
506 "the sum of whose squares is equal to 1.0.\n"
507 "Thinking of vect as coordinates in space of \n"
508 "dimension n = (vector-length vect), the coordinates\n"
509 "are uniformly distributed over the surface of the \n"
510 "unit n-shere.\n"
511 "")
512 #define FUNC_NAME s_scm_random_hollow_sphere_x
513 {
514 SCM_VALIDATE_VECTOR_OR_DVECTOR (1,v);
515 if (SCM_UNBNDP (state))
516 state = SCM_CDR (scm_var_random_state);
517 SCM_VALIDATE_RSTATE (2,state);
518 scm_random_normal_vector_x (v, state);
519 vector_scale (v, 1 / sqrt (vector_sum_squares (v)));
520 return SCM_UNSPECIFIED;
521 }
522 #undef FUNC_NAME
523
524
525 SCM_DEFINE (scm_random_normal_vector_x, "random:normal-vector!", 1, 1, 0,
526 (SCM v, SCM state),
527 "Fills vect with inexact real random numbers that are\n"
528 "independent and standard normally distributed\n"
529 "(i.e., with mean 0 and variance 1).\n"
530 "")
531 #define FUNC_NAME s_scm_random_normal_vector_x
532 {
533 int n;
534 SCM_VALIDATE_VECTOR_OR_DVECTOR (1,v);
535 if (SCM_UNBNDP (state))
536 state = SCM_CDR (scm_var_random_state);
537 SCM_VALIDATE_RSTATE (2,state);
538 n = SCM_LENGTH (v);
539 if (SCM_VECTORP (v))
540 while (--n >= 0)
541 SCM_VELTS (v)[n] = scm_makdbl (scm_c_normal01 (SCM_RSTATE (state)), 0.0);
542 else
543 while (--n >= 0)
544 ((double *) SCM_VELTS (v))[n] = scm_c_normal01 (SCM_RSTATE (state));
545 return SCM_UNSPECIFIED;
546 }
547 #undef FUNC_NAME
548
549 #endif /* HAVE_ARRAYS */
550
551 SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
552 (SCM state),
553 "Returns an inexact real in an exponential distribution with mean 1.\n"
554 "For an exponential distribution with mean u use (* u (random:exp)).\n"
555 "")
556 #define FUNC_NAME s_scm_random_exp
557 {
558 if (SCM_UNBNDP (state))
559 state = SCM_CDR (scm_var_random_state);
560 SCM_VALIDATE_RSTATE (1,state);
561 return scm_makdbl (scm_c_exp1 (SCM_RSTATE (state)), 0.0);
562 }
563 #undef FUNC_NAME
564
565 void
566 scm_init_random ()
567 {
568 int i, m;
569 /* plug in default RNG */
570 scm_rng rng =
571 {
572 sizeof (scm_i_rstate),
573 (unsigned long (*)()) scm_i_uniform32,
574 (void (*)()) scm_i_init_rstate,
575 (scm_rstate *(*)()) scm_i_copy_rstate
576 };
577 scm_the_rng = rng;
578
579 scm_tc16_rstate = scm_make_smob_type_mfpe ("random-state", 0,
580 NULL, free_rstate, NULL, NULL);
581
582 for (m = 1; m <= 0x100; m <<= 1)
583 for (i = m >> 1; i < m; ++i)
584 scm_masktab[i] = m - 1;
585
586 #include "random.x"
587
588 /* Check that the assumptions about bits per bignum digit are correct. */
589 #if SIZEOF_INT == 4
590 m = 16;
591 #else
592 m = 32;
593 #endif
594 if (m != SCM_BITSPERDIG)
595 {
596 fprintf (stderr, "Internal inconsistency: Confused about bignum digit size in random.c\n");
597 exit (1);
598 }
599
600 scm_add_feature ("random");
601 }