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