* configure.in: check for hstrerror.
[bpt/guile.git] / libguile / random.c
CommitLineData
e7a72986
MD
1/* Copyright (C) 1999 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/* Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
42
e7a72986
MD
43#include "_scm.h"
44
7f146094 45#include <stdio.h>
e7a72986
MD
46#include <math.h>
47#include "genio.h"
48#include "smob.h"
49#include "numbers.h"
50#include "feature.h"
51
52#include "random.h"
53
54\f
55/*
56 * A plugin interface for RNGs
57 *
58 * Using this interface, it is possible for the application to tell
59 * libguile to use a different RNG. This is desirable if it is
60 * necessary to use the same RNG everywhere in the application in
61 * order to prevent interference, if the application uses RNG
62 * hardware, or if the application has special demands on the RNG.
63 *
64 * Look in random.h and how the default generator is "plugged in" in
65 * scm_init_random().
66 */
67
68scm_rng scm_the_rng;
69
70\f
71/*
72 * The prepackaged RNG
73 *
74 * This is the MWC (Multiply With Carry) random number generator
75 * described by George Marsaglia at the Department of Statistics and
76 * Supercomputer Computations Research Institute, The Florida State
77 * University (http://stat.fsu.edu/~geo).
78 *
79 * It uses 64 bits, has a period of 4578426017172946943 (4.6e18), and
80 * passes all tests in the DIEHARD test suite
81 * (http://stat.fsu.edu/~geo/diehard.html)
82 */
83
84#define A 2131995753UL
85
86#if SIZEOF_LONG > 4
87#if SIZEOF_INT > 4
88#define LONG32 unsigned short
89#else
90#define LONG32 unsigned int
91#endif
92#define LONG64 unsigned long
93#else
94#define LONG32 unsigned long
95#define LONG64 unsigned long long
96#endif
97
98#if SIZEOF_LONG > 4 || defined (HAVE_LONG_LONGS)
99
100unsigned long
101scm_i_uniform32 (scm_i_rstate *state)
102{
103 LONG64 x = (LONG64) A * state->w + state->c;
104 LONG32 w = x & 0xffffffffUL;
105 state->w = w;
106 state->c = x >> 32L;
107 return w;
108}
109
110#else
111
112/* ww This is a portable version of the same RNG without 64 bit
113 * * aa arithmetic.
114 * ----
115 * xx It is only intended to provide identical behaviour on
116 * xx platforms without 8 byte longs or long longs until
117 * xx someone has implemented the routine in assembler code.
118 * xxcc
119 * ----
120 * ccww
121 */
122
123#define L(x) ((x) & 0xffff)
124#define H(x) ((x) >> 16)
125
126unsigned long
127scm_i_uniform32 (scm_i_rstate *state)
128{
129 LONG32 x1 = L (A) * L (state->w);
130 LONG32 x2 = L (A) * H (state->w);
131 LONG32 x3 = H (A) * L (state->w);
132 LONG32 w = L (x1) + L (state->c);
133 LONG32 m = H (x1) + L (x2) + L (x3) + H (state->c) + H (w);
134 LONG32 x4 = H (A) * H (state->w);
135 state->w = w = (L (m) << 16) + L (w);
136 state->c = H (x2) + H (x3) + x4 + H (m);
137 return w;
138}
139
140#endif
141
142void
143scm_i_init_rstate (scm_i_rstate *state, char *seed, int n)
144{
145 LONG32 w = 0L;
146 LONG32 c = 0L;
147 int i, m;
148 for (i = 0; i < n; ++i)
149 {
150 m = i % 8;
151 if (m < 4)
152 w += seed[i] << (8 * m);
153 else
154 c += seed[i] << (8 * (m - 4));
155 }
156 if ((w == 0 && c == 0) || (w == 0xffffffffUL && c == A - 1))
157 ++c;
158 state->w = w;
159 state->c = c;
160}
161
162scm_i_rstate *
163scm_i_copy_rstate (scm_i_rstate *state)
164{
165 scm_rstate *new_state = malloc (scm_the_rng.rstate_size);
166 if (new_state == 0)
167 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
168 (char *) SCM_NALLOC, "rstate");
169 return memcpy (new_state, state, scm_the_rng.rstate_size);
170}
171
172\f
173/*
174 * Random number library functions
175 */
176
5ee11b7c 177scm_rstate *
9b741bb6 178scm_c_make_rstate (char *seed, int n)
5ee11b7c
MD
179{
180 scm_rstate *state = malloc (scm_the_rng.rstate_size);
181 if (state == 0)
182 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
183 (char *) SCM_NALLOC,
184 "rstate");
185 state->reserved0 = 0;
186 scm_the_rng.init_rstate (state, seed, n);
187 return state;
188}
189
9b741bb6
MD
190scm_rstate *
191scm_c_default_rstate ()
192{
193 SCM state = SCM_CDR (scm_var_random_state);
194 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
195 state, "*random-state* contains bogus random state", 0);
196 return SCM_RSTATE (state);
197}
198
e7a72986 199inline double
9b741bb6 200scm_c_uniform01 (scm_rstate *state)
e7a72986 201{
5a92ddfd 202 double x = (double) scm_the_rng.random_bits (state) / (double) 0xffffffffUL;
e7a72986 203 return ((x + (double) scm_the_rng.random_bits (state))
5a92ddfd 204 / (double) 0xffffffffUL);
e7a72986
MD
205}
206
207double
9b741bb6 208scm_c_normal01 (scm_rstate *state)
e7a72986
MD
209{
210 if (state->reserved0)
211 {
212 state->reserved0 = 0;
213 return state->reserved1;
214 }
215 else
216 {
217 double r, a, n;
e7a72986 218
9b741bb6
MD
219 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
220 a = 2.0 * M_PI * scm_c_uniform01 (state);
e7a72986
MD
221
222 n = r * sin (a);
223 state->reserved1 = r * cos (a);
5a92ddfd 224 state->reserved0 = 1;
e7a72986
MD
225
226 return n;
227 }
228}
229
230double
9b741bb6 231scm_c_exp1 (scm_rstate *state)
e7a72986 232{
9b741bb6 233 return - log (scm_c_uniform01 (state));
e7a72986
MD
234}
235
236unsigned char scm_masktab[256];
237
238unsigned long
9b741bb6 239scm_c_random (scm_rstate *state, unsigned long m)
e7a72986
MD
240{
241 unsigned int r, mask;
242 mask = (m < 0x100
243 ? scm_masktab[m]
244 : (m < 0x10000
5a92ddfd 245 ? scm_masktab[m >> 8] << 8 | 0xff
e7a72986 246 : (m < 0x1000000
5a92ddfd
MD
247 ? scm_masktab[m >> 16] << 16 | 0xffff
248 : scm_masktab[m >> 24] << 24 | 0xffffff)));
e7a72986
MD
249 while ((r = scm_the_rng.random_bits (state) & mask) >= m);
250 return r;
251}
252
253SCM
9b741bb6 254scm_c_random_bignum (scm_rstate *state, SCM m)
e7a72986
MD
255{
256 SCM b;
a7e7ea3e
MD
257 int i, nd;
258 LONG32 *bits, mask, w;
259 nd = SCM_NUMDIGS (m);
260 /* calculate mask for most significant digit */
e7a72986
MD
261#if SIZEOF_INT == 4
262 /* 16 bit digits */
a7e7ea3e 263 if (nd & 1)
e7a72986
MD
264 {
265 /* fix most significant 16 bits */
a7e7ea3e 266 unsigned short s = SCM_BDIGITS (m)[nd - 1];
5a92ddfd 267 mask = s < 0x100 ? scm_masktab[s] : scm_masktab[s >> 8] << 8 | 0xff;
e7a72986
MD
268 }
269 else
270#endif
271 {
272 /* fix most significant 32 bits */
96e263d6 273#if SIZEOF_INT == 4
2a0279c9
MD
274 w = SCM_BDIGITS (m)[nd - 1] << 16 | SCM_BDIGITS (m)[nd - 2];
275#else
96e263d6 276 w = SCM_BDIGITS (m)[nd - 1];
2a0279c9 277#endif
e7a72986
MD
278 mask = (w < 0x10000
279 ? (w < 0x100
280 ? scm_masktab[w]
5a92ddfd 281 : scm_masktab[w >> 8] << 8 | 0xff)
e7a72986 282 : (w < 0x1000000
5a92ddfd
MD
283 ? scm_masktab[w >> 16] << 16 | 0xffff
284 : scm_masktab[w >> 24] << 24 | 0xffffff));
e7a72986 285 }
a7e7ea3e
MD
286 b = scm_mkbig (nd, 0);
287 bits = (LONG32 *) SCM_BDIGITS (b);
288 do
e7a72986 289 {
a7e7ea3e
MD
290 i = nd;
291 /* treat most significant digit specially */
292#if SIZEOF_INT == 4
293 /* 16 bit digits */
294 if (i & 1)
295 {
296 ((SCM_BIGDIG*) bits)[i - 1] = scm_the_rng.random_bits (state) & mask;
297 i /= 2;
298 }
299 else
300#endif
301 {
302 /* fix most significant 32 bits */
96e263d6 303#if SIZEOF_INT == 4
2a0279c9 304 w = scm_the_rng.random_bits (state) & mask;
96e263d6
MD
305 ((SCM_BIGDIG*) bits)[i - 2] = w & 0xffff;
306 ((SCM_BIGDIG*) bits)[i - 1] = w >> 16;
307 i = i / 2 - 1;
2a0279c9 308#else
96e263d6 309 i /= 2;
a7e7ea3e 310 bits[--i] = scm_the_rng.random_bits (state) & mask;
2a0279c9 311#endif
a7e7ea3e
MD
312 }
313 /* now fill up the rest of the bignum */
314 while (i)
315 bits[--i] = scm_the_rng.random_bits (state);
316 b = scm_normbig (b);
317 if (SCM_INUMP (b))
318 return b;
319 } while (scm_bigcomp (b, m) <= 0);
320 return b;
e7a72986
MD
321}
322
323/*
324 * Scheme level representation of random states.
325 */
326
327long scm_tc16_rstate;
328
329static SCM
330make_rstate (scm_rstate *state)
331{
23a62151 332 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
e7a72986
MD
333}
334
335static scm_sizet
336free_rstate (SCM rstate)
337{
338 free (SCM_RSTATE (rstate));
339 return scm_the_rng.rstate_size;
340}
341
e7a72986
MD
342/*
343 * Scheme level interface.
344 */
345
5ee11b7c 346SCM_GLOBAL_VCELL_INIT (scm_var_random_state, "*random-state*", scm_seed_to_random_state (scm_makfrom0str ("URL:http://stat.fsu.edu/~geo/diehard.html")));
e7a72986
MD
347
348SCM_PROC (s_random, "random", 1, 1, 0, scm_random);
349
350SCM
351scm_random (SCM n, SCM state)
352{
353 if (SCM_UNBNDP (state))
354 state = SCM_CDR (scm_var_random_state);
355 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
356 state, SCM_ARG2, s_random);
357 if (SCM_INUMP (n))
358 {
359 unsigned long m = SCM_INUM (n);
360 SCM_ASSERT (m > 0, n, SCM_ARG1, s_random);
9b741bb6 361 return SCM_MAKINUM (scm_c_random (SCM_RSTATE (state), m));
e7a72986
MD
362 }
363 SCM_ASSERT (SCM_NIMP (n), n, SCM_ARG1, s_random);
364 if (SCM_REALP (n))
9b741bb6 365 return scm_makdbl (SCM_REALPART (n) * scm_c_uniform01 (SCM_RSTATE (state)),
e7a72986
MD
366 0.0);
367 SCM_ASSERT (SCM_TYP16 (n) == scm_tc16_bigpos, n, SCM_ARG1, s_random);
9b741bb6 368 return scm_c_random_bignum (SCM_RSTATE (state), n);
e7a72986
MD
369}
370
5ee11b7c 371SCM_PROC (s_copy_random_state, "copy-random-state", 0, 1, 0, scm_copy_random_state);
e7a72986
MD
372
373SCM
5ee11b7c 374scm_copy_random_state (SCM state)
e7a72986
MD
375{
376 if (SCM_UNBNDP (state))
5ee11b7c 377 state = SCM_CDR (scm_var_random_state);
e7a72986
MD
378 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
379 state,
380 SCM_ARG1,
5ee11b7c 381 s_copy_random_state);
e7a72986
MD
382 return make_rstate (scm_the_rng.copy_rstate (SCM_RSTATE (state)));
383}
384
5ee11b7c
MD
385SCM_PROC (s_seed_to_random_state, "seed->random-state", 1, 0, 0, scm_seed_to_random_state);
386
387SCM
388scm_seed_to_random_state (SCM seed)
389{
390 if (SCM_NUMBERP (seed))
391 seed = scm_number_to_string (seed, SCM_UNDEFINED);
392 SCM_ASSERT (SCM_NIMP (seed) && SCM_STRINGP (seed),
393 seed,
394 SCM_ARG1,
395 s_seed_to_random_state);
9b741bb6 396 return make_rstate (scm_c_make_rstate (SCM_ROCHARS (seed),
5ee11b7c
MD
397 SCM_LENGTH (seed)));
398}
399
e7a72986
MD
400SCM_PROC (s_random_uniform, "random:uniform", 0, 1, 0, scm_random_uniform);
401
402SCM
403scm_random_uniform (SCM state)
404{
405 if (SCM_UNBNDP (state))
406 state = SCM_CDR (scm_var_random_state);
407 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
408 state,
409 SCM_ARG1,
410 s_random_uniform);
9b741bb6 411 return scm_makdbl (scm_c_uniform01 (SCM_RSTATE (state)), 0.0);
e7a72986
MD
412}
413
414static void
415vector_scale (SCM v, double c)
416{
417 int n = SCM_LENGTH (v);
418 if (SCM_VECTORP (v))
419 while (--n >= 0)
420 SCM_REAL (SCM_VELTS (v)[n]) *= c;
421 else
422 while (--n >= 0)
423 ((double *) SCM_VELTS (v))[n] *= c;
424}
425
426static double
427vector_sum_squares (SCM v)
428{
429 double x, sum = 0.0;
430 int n = SCM_LENGTH (v);
431 if (SCM_VECTORP (v))
432 while (--n >= 0)
433 {
434 x = SCM_REAL (SCM_VELTS (v)[n]);
435 sum += x * x;
436 }
437 else
438 while (--n >= 0)
439 {
440 x = ((double *) SCM_VELTS (v))[n];
441 sum += x * x;
442 }
443 return sum;
444}
445
446SCM_PROC (s_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0, scm_random_solid_sphere_x);
447
448/* For the uniform distribution on the solid sphere, note that in
449 * this distribution the length r of the vector has cumulative
450 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
451 * generated as r=u^(1/n).
452 */
453SCM
454scm_random_solid_sphere_x (SCM v, SCM state)
455{
456 SCM_ASSERT (SCM_NIMP (v)
457 && (SCM_VECTORP (v) || SCM_TYP7 (v) == scm_tc7_dvect),
458 v, SCM_ARG1, s_random_solid_sphere_x);
459 if (SCM_UNBNDP (state))
460 state = SCM_CDR (scm_var_random_state);
461 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
462 state,
463 SCM_ARG2,
464 s_random_solid_sphere_x);
465 scm_random_normal_vector_x (v, state);
466 vector_scale (v,
9b741bb6 467 pow (scm_c_uniform01 (SCM_RSTATE (state)),
e7a72986
MD
468 1.0 / SCM_LENGTH (v))
469 / sqrt (vector_sum_squares (v)));
470 return SCM_UNSPECIFIED;
471}
472
473SCM_PROC (s_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0, scm_random_hollow_sphere_x);
474
475SCM
476scm_random_hollow_sphere_x (SCM v, SCM state)
477{
478 SCM_ASSERT (SCM_NIMP (v)
479 && (SCM_VECTORP (v) || SCM_TYP7 (v) == scm_tc7_dvect),
480 v, SCM_ARG1, s_random_solid_sphere_x);
481 if (SCM_UNBNDP (state))
482 state = SCM_CDR (scm_var_random_state);
483 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
484 state,
485 SCM_ARG2,
486 s_random_hollow_sphere_x);
487 scm_random_normal_vector_x (v, state);
488 vector_scale (v, 1 / sqrt (vector_sum_squares (v)));
489 return SCM_UNSPECIFIED;
490}
491
492SCM_PROC (s_random_normal, "random:normal", 0, 1, 0, scm_random_normal);
493
494SCM
495scm_random_normal (SCM state)
496{
497 if (SCM_UNBNDP (state))
498 state = SCM_CDR (scm_var_random_state);
499 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
500 state,
501 SCM_ARG1,
502 s_random_normal);
9b741bb6 503 return scm_makdbl (scm_c_normal01 (SCM_RSTATE (state)), 0.0);
e7a72986
MD
504}
505
506SCM_PROC (s_random_normal_vector_x, "random:normal-vector!", 1, 1, 0, scm_random_normal_vector_x);
507
508SCM
509scm_random_normal_vector_x (SCM v, SCM state)
510{
511 int n;
512 SCM_ASSERT (SCM_NIMP (v)
513 && (SCM_VECTORP (v) || SCM_TYP7 (v) == scm_tc7_dvect),
514 v, SCM_ARG1, s_random_solid_sphere_x);
515 if (SCM_UNBNDP (state))
516 state = SCM_CDR (scm_var_random_state);
517 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
518 state,
519 SCM_ARG2,
520 s_random_normal_vector_x);
521 n = SCM_LENGTH (v);
522 if (SCM_VECTORP (v))
523 while (--n >= 0)
9b741bb6 524 SCM_VELTS (v)[n] = scm_makdbl (scm_c_normal01 (SCM_RSTATE (state)), 0.0);
e7a72986
MD
525 else
526 while (--n >= 0)
9b741bb6 527 ((double *) SCM_VELTS (v))[n] = scm_c_normal01 (SCM_RSTATE (state));
e7a72986
MD
528 return SCM_UNSPECIFIED;
529}
530
531SCM_PROC (s_random_exp, "random:exp", 0, 1, 0, scm_random_exp);
532
533SCM
534scm_random_exp (SCM state)
535{
536 if (SCM_UNBNDP (state))
537 state = SCM_CDR (scm_var_random_state);
538 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
539 state,
540 SCM_ARG1,
541 s_random_exp);
9b741bb6 542 return scm_makdbl (scm_c_exp1 (SCM_RSTATE (state)), 0.0);
e7a72986
MD
543}
544
545void
546scm_init_random ()
547{
548 int i, m;
549 /* plug in default RNG */
550 scm_rng rng =
551 {
552 sizeof (scm_i_rstate),
553 (unsigned long (*)()) scm_i_uniform32,
554 (void (*)()) scm_i_init_rstate,
555 (scm_rstate *(*)()) scm_i_copy_rstate
556 };
557 scm_the_rng = rng;
558
23a62151
MD
559 scm_tc16_rstate = scm_make_smob_type_mfpe ("random-state", 0,
560 NULL, free_rstate, NULL, NULL);
e7a72986
MD
561
562 for (m = 1; m <= 0x100; m <<= 1)
563 for (i = m >> 1; i < m; ++i)
564 scm_masktab[i] = m - 1;
565
566#include "random.x"
567
2a0279c9
MD
568 /* Check that the assumptions about bits per bignum digit are correct. */
569#if SIZEOF_INT == 4
570 m = 16;
571#else
572 m = 32;
573#endif
574 if (m != SCM_BITSPERDIG)
575 {
576 fprintf (stderr, "Internal inconsistency: Confused about bignum digit size in random.c\n");
577 exit (1);
578 }
579
e7a72986
MD
580 scm_add_feature ("random");
581}