Update copyright dates on random.c
[bpt/guile.git] / libguile / random.c
1 /* Copyright (C) 1999, 2000, 2001, 2003, 2005, 2006, 2009, 2010,
2 * 2012, 2013 Free Software Foundation, Inc.
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
17 */
18
19
20
21 /* Original Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include "libguile/_scm.h"
28
29 #include <gmp.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <string.h>
33 #include <sys/types.h>
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #include "libguile/smob.h"
40 #include "libguile/numbers.h"
41 #include "libguile/feature.h"
42 #include "libguile/strings.h"
43 #include "libguile/arrays.h"
44 #include "libguile/srfi-4.h"
45 #include "libguile/vectors.h"
46 #include "libguile/generalized-vectors.h"
47
48 #include "libguile/validate.h"
49 #include "libguile/random.h"
50
51 \f
52 /*
53 * A plugin interface for RNGs
54 *
55 * Using this interface, it is possible for the application to tell
56 * libguile to use a different RNG. This is desirable if it is
57 * necessary to use the same RNG everywhere in the application in
58 * order to prevent interference, if the application uses RNG
59 * hardware, or if the application has special demands on the RNG.
60 *
61 * Look in random.h and how the default generator is "plugged in" in
62 * scm_init_random().
63 */
64
65 scm_t_rng scm_the_rng;
66
67 \f
68 /*
69 * The prepackaged RNG
70 *
71 * This is the MWC (Multiply With Carry) random number generator
72 * described by George Marsaglia at the Department of Statistics and
73 * Supercomputer Computations Research Institute, The Florida State
74 * University (http://stat.fsu.edu/~geo).
75 *
76 * It uses 64 bits, has a period of 4578426017172946943 (4.6e18), and
77 * passes all tests in the DIEHARD test suite
78 * (http://stat.fsu.edu/~geo/diehard.html)
79 */
80
81 typedef struct scm_t_i_rstate {
82 scm_t_rstate rstate;
83 scm_t_uint32 w;
84 scm_t_uint32 c;
85 } scm_t_i_rstate;
86
87
88 #define A 2131995753UL
89
90 #ifndef M_PI
91 #define M_PI 3.14159265359
92 #endif
93
94 static scm_t_uint32
95 scm_i_uniform32 (scm_t_rstate *state)
96 {
97 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
98 scm_t_uint64 x = (scm_t_uint64) A * istate->w + istate->c;
99 scm_t_uint32 w = x & 0xffffffffUL;
100 istate->w = w;
101 istate->c = x >> 32L;
102 return w;
103 }
104
105 static void
106 scm_i_init_rstate (scm_t_rstate *state, const char *seed, int n)
107 {
108 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
109 scm_t_uint32 w = 0L;
110 scm_t_uint32 c = 0L;
111 int i, m;
112 for (i = 0; i < n; ++i)
113 {
114 m = i % 8;
115 if (m < 4)
116 w += seed[i] << (8 * m);
117 else
118 c += seed[i] << (8 * (m - 4));
119 }
120 if ((w == 0 && c == 0) || (w == -1 && c == A - 1))
121 ++c;
122 istate->w = w;
123 istate->c = c;
124 }
125
126 static scm_t_rstate *
127 scm_i_copy_rstate (scm_t_rstate *state)
128 {
129 scm_t_rstate *new_state;
130
131 new_state = scm_gc_malloc_pointerless (state->rng->rstate_size,
132 "random-state");
133 return memcpy (new_state, state, state->rng->rstate_size);
134 }
135
136 SCM_SYMBOL(scm_i_rstate_tag, "multiply-with-carry");
137
138 static void
139 scm_i_rstate_from_datum (scm_t_rstate *state, SCM value)
140 #define FUNC_NAME "scm_i_rstate_from_datum"
141 {
142 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
143 scm_t_uint32 w, c;
144 long length;
145
146 SCM_VALIDATE_LIST_COPYLEN (SCM_ARG1, value, length);
147 SCM_ASSERT (length == 3, value, SCM_ARG1, FUNC_NAME);
148 SCM_ASSERT (scm_is_eq (SCM_CAR (value), scm_i_rstate_tag),
149 value, SCM_ARG1, FUNC_NAME);
150 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADR (value), w);
151 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADDR (value), c);
152
153 istate->w = w;
154 istate->c = c;
155 }
156 #undef FUNC_NAME
157
158 static SCM
159 scm_i_rstate_to_datum (scm_t_rstate *state)
160 {
161 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
162 return scm_list_3 (scm_i_rstate_tag,
163 scm_from_uint32 (istate->w),
164 scm_from_uint32 (istate->c));
165 }
166
167 \f
168 /*
169 * Random number library functions
170 */
171
172 scm_t_rstate *
173 scm_c_make_rstate (const char *seed, int n)
174 {
175 scm_t_rstate *state;
176
177 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
178 "random-state");
179 state->rng = &scm_the_rng;
180 state->normal_next = 0.0;
181 state->rng->init_rstate (state, seed, n);
182 return state;
183 }
184
185 scm_t_rstate *
186 scm_c_rstate_from_datum (SCM datum)
187 {
188 scm_t_rstate *state;
189
190 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
191 "random-state");
192 state->rng = &scm_the_rng;
193 state->normal_next = 0.0;
194 state->rng->from_datum (state, datum);
195 return state;
196 }
197
198 scm_t_rstate *
199 scm_c_default_rstate ()
200 #define FUNC_NAME "scm_c_default_rstate"
201 {
202 SCM state = SCM_VARIABLE_REF (scm_var_random_state);
203 if (!SCM_RSTATEP (state))
204 SCM_MISC_ERROR ("*random-state* contains bogus random state", SCM_EOL);
205 return SCM_RSTATE (state);
206 }
207 #undef FUNC_NAME
208
209
210 double
211 scm_c_uniform01 (scm_t_rstate *state)
212 {
213 double x = (double) state->rng->random_bits (state) / (double) 0xffffffffUL;
214 return ((x + (double) state->rng->random_bits (state))
215 / (double) 0xffffffffUL);
216 }
217
218 double
219 scm_c_normal01 (scm_t_rstate *state)
220 {
221 if (state->normal_next != 0.0)
222 {
223 double ret = state->normal_next;
224
225 state->normal_next = 0.0;
226
227 return ret;
228 }
229 else
230 {
231 double r, a, n;
232
233 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
234 a = 2.0 * M_PI * scm_c_uniform01 (state);
235
236 n = r * sin (a);
237 state->normal_next = r * cos (a);
238
239 return n;
240 }
241 }
242
243 double
244 scm_c_exp1 (scm_t_rstate *state)
245 {
246 return - log (scm_c_uniform01 (state));
247 }
248
249 unsigned char scm_masktab[256];
250
251 static inline scm_t_uint32
252 scm_i_mask32 (scm_t_uint32 m)
253 {
254 return (m < 0x100
255 ? scm_masktab[m]
256 : (m < 0x10000
257 ? scm_masktab[m >> 8] << 8 | 0xff
258 : (m < 0x1000000
259 ? scm_masktab[m >> 16] << 16 | 0xffff
260 : scm_masktab[m >> 24] << 24 | 0xffffff)));
261 }
262
263 scm_t_uint32
264 scm_c_random (scm_t_rstate *state, scm_t_uint32 m)
265 {
266 scm_t_uint32 r, mask = scm_i_mask32 (m);
267 while ((r = state->rng->random_bits (state) & mask) >= m);
268 return r;
269 }
270
271 scm_t_uint64
272 scm_c_random64 (scm_t_rstate *state, scm_t_uint64 m)
273 {
274 scm_t_uint64 r;
275 scm_t_uint32 mask;
276
277 if (m <= SCM_T_UINT32_MAX)
278 return scm_c_random (state, (scm_t_uint32) m);
279
280 mask = scm_i_mask32 (m >> 32);
281 while ((r = ((scm_t_uint64) (state->rng->random_bits (state) & mask) << 32)
282 | state->rng->random_bits (state)) >= m)
283 ;
284 return r;
285 }
286
287 /*
288 SCM scm_c_random_bignum (scm_t_rstate *state, SCM m)
289
290 Takes a random state (source of random bits) and a bignum m.
291 Returns a bignum b, 0 <= b < m.
292
293 It does this by allocating a bignum b with as many base 65536 digits
294 as m, filling b with random bits (in 32 bit chunks) up to the most
295 significant 1 in m, and, finally checking if the resultant b is too
296 large (>= m). If too large, we simply repeat the process again. (It
297 is important to throw away all generated random bits if b >= m,
298 otherwise we'll end up with a distorted distribution.)
299
300 */
301
302 SCM
303 scm_c_random_bignum (scm_t_rstate *state, SCM m)
304 {
305 SCM result = scm_i_mkbig ();
306 const size_t m_bits = mpz_sizeinbase (SCM_I_BIG_MPZ (m), 2);
307 /* how many bits would only partially fill the last scm_t_uint32? */
308 const size_t end_bits = m_bits % (sizeof (scm_t_uint32) * SCM_CHAR_BIT);
309 scm_t_uint32 *random_chunks = NULL;
310 const scm_t_uint32 num_full_chunks =
311 m_bits / (sizeof (scm_t_uint32) * SCM_CHAR_BIT);
312 const scm_t_uint32 num_chunks = num_full_chunks + ((end_bits) ? 1 : 0);
313
314 /* we know the result will be this big */
315 mpz_realloc2 (SCM_I_BIG_MPZ (result), m_bits);
316
317 random_chunks =
318 (scm_t_uint32 *) scm_gc_calloc (num_chunks * sizeof (scm_t_uint32),
319 "random bignum chunks");
320
321 do
322 {
323 scm_t_uint32 *current_chunk = random_chunks + (num_chunks - 1);
324 scm_t_uint32 chunks_left = num_chunks;
325
326 mpz_set_ui (SCM_I_BIG_MPZ (result), 0);
327
328 if (end_bits)
329 {
330 /* generate a mask with ones in the end_bits position, i.e. if
331 end_bits is 3, then we'd have a mask of ...0000000111 */
332 const scm_t_uint32 rndbits = state->rng->random_bits (state);
333 int rshift = (sizeof (scm_t_uint32) * SCM_CHAR_BIT) - end_bits;
334 scm_t_uint32 mask = ((scm_t_uint32)-1) >> rshift;
335 scm_t_uint32 highest_bits = rndbits & mask;
336 *current_chunk-- = highest_bits;
337 chunks_left--;
338 }
339
340 while (chunks_left)
341 {
342 /* now fill in the remaining scm_t_uint32 sized chunks */
343 *current_chunk-- = state->rng->random_bits (state);
344 chunks_left--;
345 }
346 mpz_import (SCM_I_BIG_MPZ (result),
347 num_chunks,
348 -1,
349 sizeof (scm_t_uint32),
350 0,
351 0,
352 random_chunks);
353 /* if result >= m, regenerate it (it is important to regenerate
354 all bits in order not to get a distorted distribution) */
355 } while (mpz_cmp (SCM_I_BIG_MPZ (result), SCM_I_BIG_MPZ (m)) >= 0);
356 scm_gc_free (random_chunks,
357 num_chunks * sizeof (scm_t_uint32),
358 "random bignum chunks");
359 return scm_i_normbig (result);
360 }
361
362 /*
363 * Scheme level representation of random states.
364 */
365
366 scm_t_bits scm_tc16_rstate;
367
368 static SCM
369 make_rstate (scm_t_rstate *state)
370 {
371 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
372 }
373
374
375 /*
376 * Scheme level interface.
377 */
378
379 SCM_GLOBAL_VARIABLE_INIT (scm_var_random_state, "*random-state*", scm_seed_to_random_state (scm_from_locale_string ("URL:http://stat.fsu.edu/~geo/diehard.html")));
380
381 SCM_DEFINE (scm_random, "random", 1, 1, 0,
382 (SCM n, SCM state),
383 "Return a number in [0, N).\n"
384 "\n"
385 "Accepts a positive integer or real n and returns a\n"
386 "number of the same type between zero (inclusive) and\n"
387 "N (exclusive). The values returned have a uniform\n"
388 "distribution.\n"
389 "\n"
390 "The optional argument @var{state} must be of the type produced\n"
391 "by @code{seed->random-state}. It defaults to the value of the\n"
392 "variable @var{*random-state*}. This object is used to maintain\n"
393 "the state of the pseudo-random-number generator and is altered\n"
394 "as a side effect of the random operation.")
395 #define FUNC_NAME s_scm_random
396 {
397 if (SCM_UNBNDP (state))
398 state = SCM_VARIABLE_REF (scm_var_random_state);
399 SCM_VALIDATE_RSTATE (2, state);
400 if (SCM_I_INUMP (n))
401 {
402 scm_t_bits m = (scm_t_bits) SCM_I_INUM (n);
403 SCM_ASSERT_RANGE (1, n, SCM_I_INUM (n) > 0);
404 #if SCM_SIZEOF_UINTPTR_T <= 4
405 return scm_from_uint32 (scm_c_random (SCM_RSTATE (state),
406 (scm_t_uint32) m));
407 #elif SCM_SIZEOF_UINTPTR_T <= 8
408 return scm_from_uint64 (scm_c_random64 (SCM_RSTATE (state),
409 (scm_t_uint64) m));
410 #else
411 #error "Cannot deal with this platform's scm_t_bits size"
412 #endif
413 }
414 SCM_VALIDATE_NIM (1, n);
415 if (SCM_REALP (n))
416 return scm_from_double (SCM_REAL_VALUE (n)
417 * scm_c_uniform01 (SCM_RSTATE (state)));
418
419 if (!SCM_BIGP (n))
420 SCM_WRONG_TYPE_ARG (1, n);
421 return scm_c_random_bignum (SCM_RSTATE (state), n);
422 }
423 #undef FUNC_NAME
424
425 SCM_DEFINE (scm_copy_random_state, "copy-random-state", 0, 1, 0,
426 (SCM state),
427 "Return a copy of the random state @var{state}.")
428 #define FUNC_NAME s_scm_copy_random_state
429 {
430 if (SCM_UNBNDP (state))
431 state = SCM_VARIABLE_REF (scm_var_random_state);
432 SCM_VALIDATE_RSTATE (1, state);
433 return make_rstate (SCM_RSTATE (state)->rng->copy_rstate (SCM_RSTATE (state)));
434 }
435 #undef FUNC_NAME
436
437 SCM_DEFINE (scm_seed_to_random_state, "seed->random-state", 1, 0, 0,
438 (SCM seed),
439 "Return a new random state using @var{seed}.")
440 #define FUNC_NAME s_scm_seed_to_random_state
441 {
442 SCM res;
443 if (SCM_NUMBERP (seed))
444 seed = scm_number_to_string (seed, SCM_UNDEFINED);
445 SCM_VALIDATE_STRING (1, seed);
446 res = make_rstate (scm_c_make_rstate (scm_i_string_chars (seed),
447 scm_i_string_length (seed)));
448 scm_remember_upto_here_1 (seed);
449 return res;
450
451 }
452 #undef FUNC_NAME
453
454 SCM_DEFINE (scm_datum_to_random_state, "datum->random-state", 1, 0, 0,
455 (SCM datum),
456 "Return a new random state using @var{datum}, which should have\n"
457 "been obtained from @code{random-state->datum}.")
458 #define FUNC_NAME s_scm_datum_to_random_state
459 {
460 return make_rstate (scm_c_rstate_from_datum (datum));
461 }
462 #undef FUNC_NAME
463
464 SCM_DEFINE (scm_random_state_to_datum, "random-state->datum", 1, 0, 0,
465 (SCM state),
466 "Return a datum representation of @var{state} that may be\n"
467 "written out and read back with the Scheme reader.")
468 #define FUNC_NAME s_scm_random_state_to_datum
469 {
470 SCM_VALIDATE_RSTATE (1, state);
471 return SCM_RSTATE (state)->rng->to_datum (SCM_RSTATE (state));
472 }
473 #undef FUNC_NAME
474
475 SCM_DEFINE (scm_random_uniform, "random:uniform", 0, 1, 0,
476 (SCM state),
477 "Return a uniformly distributed inexact real random number in\n"
478 "[0,1).")
479 #define FUNC_NAME s_scm_random_uniform
480 {
481 if (SCM_UNBNDP (state))
482 state = SCM_VARIABLE_REF (scm_var_random_state);
483 SCM_VALIDATE_RSTATE (1, state);
484 return scm_from_double (scm_c_uniform01 (SCM_RSTATE (state)));
485 }
486 #undef FUNC_NAME
487
488 SCM_DEFINE (scm_random_normal, "random:normal", 0, 1, 0,
489 (SCM state),
490 "Return an inexact real in a normal distribution. The\n"
491 "distribution used has mean 0 and standard deviation 1. For a\n"
492 "normal distribution with mean m and standard deviation d use\n"
493 "@code{(+ m (* d (random:normal)))}.")
494 #define FUNC_NAME s_scm_random_normal
495 {
496 if (SCM_UNBNDP (state))
497 state = SCM_VARIABLE_REF (scm_var_random_state);
498 SCM_VALIDATE_RSTATE (1, state);
499 return scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
500 }
501 #undef FUNC_NAME
502
503 static void
504 vector_scale_x (SCM v, double c)
505 {
506 size_t n;
507 if (scm_is_simple_vector (v))
508 {
509 n = SCM_SIMPLE_VECTOR_LENGTH (v);
510 while (n-- > 0)
511 SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n)) *= c;
512 }
513 else
514 {
515 /* must be a f64vector. */
516 scm_t_array_handle handle;
517 size_t i, len;
518 ssize_t inc;
519 double *elts;
520
521 elts = scm_f64vector_writable_elements (v, &handle, &len, &inc);
522
523 for (i = 0; i < len; i++, elts += inc)
524 *elts *= c;
525
526 scm_array_handle_release (&handle);
527 }
528 }
529
530 static double
531 vector_sum_squares (SCM v)
532 {
533 double x, sum = 0.0;
534 size_t n;
535 if (scm_is_simple_vector (v))
536 {
537 n = SCM_SIMPLE_VECTOR_LENGTH (v);
538 while (n-- > 0)
539 {
540 x = SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n));
541 sum += x * x;
542 }
543 }
544 else
545 {
546 /* must be a f64vector. */
547 scm_t_array_handle handle;
548 size_t i, len;
549 ssize_t inc;
550 const double *elts;
551
552 elts = scm_f64vector_elements (v, &handle, &len, &inc);
553
554 for (i = 0; i < len; i++, elts += inc)
555 {
556 x = *elts;
557 sum += x * x;
558 }
559
560 scm_array_handle_release (&handle);
561 }
562 return sum;
563 }
564
565 /* For the uniform distribution on the solid sphere, note that in
566 * this distribution the length r of the vector has cumulative
567 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
568 * generated as r=u^(1/n).
569 */
570 SCM_DEFINE (scm_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0,
571 (SCM v, SCM state),
572 "Fills @var{vect} with inexact real random numbers the sum of\n"
573 "whose squares is less than 1.0. Thinking of @var{vect} as\n"
574 "coordinates in space of dimension @var{n} @math{=}\n"
575 "@code{(vector-length @var{vect})}, the coordinates are\n"
576 "uniformly distributed within the unit @var{n}-sphere.")
577 #define FUNC_NAME s_scm_random_solid_sphere_x
578 {
579 if (SCM_UNBNDP (state))
580 state = SCM_VARIABLE_REF (scm_var_random_state);
581 SCM_VALIDATE_RSTATE (2, state);
582 scm_random_normal_vector_x (v, state);
583 vector_scale_x (v,
584 pow (scm_c_uniform01 (SCM_RSTATE (state)),
585 1.0 / scm_c_generalized_vector_length (v))
586 / sqrt (vector_sum_squares (v)));
587 return SCM_UNSPECIFIED;
588 }
589 #undef FUNC_NAME
590
591 SCM_DEFINE (scm_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0,
592 (SCM v, SCM state),
593 "Fills vect with inexact real random numbers\n"
594 "the sum of whose squares is equal to 1.0.\n"
595 "Thinking of vect as coordinates in space of\n"
596 "dimension n = (vector-length vect), the coordinates\n"
597 "are uniformly distributed over the surface of the\n"
598 "unit n-sphere.")
599 #define FUNC_NAME s_scm_random_hollow_sphere_x
600 {
601 if (SCM_UNBNDP (state))
602 state = SCM_VARIABLE_REF (scm_var_random_state);
603 SCM_VALIDATE_RSTATE (2, state);
604 scm_random_normal_vector_x (v, state);
605 vector_scale_x (v, 1 / sqrt (vector_sum_squares (v)));
606 return SCM_UNSPECIFIED;
607 }
608 #undef FUNC_NAME
609
610
611 SCM_DEFINE (scm_random_normal_vector_x, "random:normal-vector!", 1, 1, 0,
612 (SCM v, SCM state),
613 "Fills vect with inexact real random numbers that are\n"
614 "independent and standard normally distributed\n"
615 "(i.e., with mean 0 and variance 1).")
616 #define FUNC_NAME s_scm_random_normal_vector_x
617 {
618 long i;
619 scm_t_array_handle handle;
620 scm_t_array_dim *dim;
621
622 if (SCM_UNBNDP (state))
623 state = SCM_VARIABLE_REF (scm_var_random_state);
624 SCM_VALIDATE_RSTATE (2, state);
625
626 scm_generalized_vector_get_handle (v, &handle);
627 dim = scm_array_handle_dims (&handle);
628
629 if (scm_is_vector (v))
630 {
631 SCM *elts = scm_array_handle_writable_elements (&handle);
632 for (i = dim->lbnd; i <= dim->ubnd; i++, elts += dim->inc)
633 *elts = scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
634 }
635 else
636 {
637 /* must be a f64vector. */
638 double *elts = scm_array_handle_f64_writable_elements (&handle);
639 for (i = dim->lbnd; i <= dim->ubnd; i++, elts += dim->inc)
640 *elts = scm_c_normal01 (SCM_RSTATE (state));
641 }
642
643 scm_array_handle_release (&handle);
644
645 return SCM_UNSPECIFIED;
646 }
647 #undef FUNC_NAME
648
649 SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
650 (SCM state),
651 "Return an inexact real in an exponential distribution with mean\n"
652 "1. For an exponential distribution with mean u use (* u\n"
653 "(random:exp)).")
654 #define FUNC_NAME s_scm_random_exp
655 {
656 if (SCM_UNBNDP (state))
657 state = SCM_VARIABLE_REF (scm_var_random_state);
658 SCM_VALIDATE_RSTATE (1, state);
659 return scm_from_double (scm_c_exp1 (SCM_RSTATE (state)));
660 }
661 #undef FUNC_NAME
662
663 /* Return a new random-state seeded from the time, date, process ID, an
664 address from a freshly allocated heap cell, an address from the local
665 stack frame, and a high-resolution timer if available. This is only
666 to be used as a last resort, when no better source of entropy is
667 available. */
668 static SCM
669 random_state_of_last_resort (void)
670 {
671 SCM state;
672 SCM time_of_day = scm_gettimeofday ();
673 SCM sources = scm_list_n
674 (scm_from_unsigned_integer (SCM_UNPACK (time_of_day)), /* heap addr */
675 /* Avoid scm_getpid, since it depends on HAVE_POSIX. */
676 scm_from_unsigned_integer (getpid ()), /* process ID */
677 scm_get_internal_real_time (), /* high-resolution process timer */
678 scm_from_unsigned_integer ((scm_t_bits) &time_of_day), /* stack addr */
679 scm_car (time_of_day), /* seconds since midnight 1970-01-01 UTC */
680 scm_cdr (time_of_day), /* microsecond component of the above clock */
681 SCM_UNDEFINED);
682
683 /* Concatenate the sources bitwise to form the seed */
684 SCM seed = SCM_INUM0;
685 while (scm_is_pair (sources))
686 {
687 seed = scm_logxor (seed, scm_ash (scm_car (sources),
688 scm_integer_length (seed)));
689 sources = scm_cdr (sources);
690 }
691
692 /* FIXME The following code belongs in `scm_seed_to_random_state',
693 and here we should simply do:
694
695 return scm_seed_to_random_state (seed);
696
697 Unfortunately, `scm_seed_to_random_state' only preserves around 32
698 bits of entropy from the provided seed. I don't know if it's okay
699 to fix that in 2.0, so for now we have this workaround. */
700 {
701 int i, len;
702 unsigned char *buf;
703 len = scm_to_int (scm_ceiling_quotient (scm_integer_length (seed),
704 SCM_I_MAKINUM (8)));
705 buf = (unsigned char *) malloc (len);
706 for (i = len-1; i >= 0; --i)
707 {
708 buf[i] = scm_to_int (scm_logand (seed, SCM_I_MAKINUM (255)));
709 seed = scm_ash (seed, SCM_I_MAKINUM (-8));
710 }
711 state = make_rstate (scm_c_make_rstate ((char *) buf, len));
712 free (buf);
713 }
714 return state;
715 }
716
717 /* Attempt to fill buffer with random bytes from /dev/urandom.
718 Return 1 if successful, else return 0. */
719 static int
720 read_dev_urandom (unsigned char *buf, size_t len)
721 {
722 size_t res = 0;
723 FILE *f = fopen ("/dev/urandom", "r");
724 if (f)
725 {
726 res = fread(buf, 1, len, f);
727 fclose (f);
728 }
729 return (res == len);
730 }
731
732 /* Fill a buffer with random bytes seeded from a platform-specific
733 source of entropy. /dev/urandom is used if available. Note that
734 this function provides no guarantees about the amount of entropy
735 present in the returned bytes. */
736 void
737 scm_i_random_bytes_from_platform (unsigned char *buf, size_t len)
738 {
739 if (read_dev_urandom (buf, len))
740 return;
741 else /* FIXME: support other platform sources */
742 {
743 /* When all else fails, use this (rather weak) fallback */
744 SCM random_state = random_state_of_last_resort ();
745 int i;
746 for (i = len-1; i >= 0; --i)
747 buf[i] = scm_to_int (scm_random (SCM_I_MAKINUM (256), random_state));
748 }
749 }
750
751 SCM_DEFINE (scm_random_state_from_platform, "random-state-from-platform", 0, 0, 0,
752 (void),
753 "Construct a new random state seeded from a platform-specific\n\
754 source of entropy, appropriate for use in non-security-critical applications.")
755 #define FUNC_NAME s_scm_random_state_from_platform
756 {
757 unsigned char buf[32];
758 if (read_dev_urandom (buf, sizeof(buf)))
759 return make_rstate (scm_c_make_rstate ((char *) buf, sizeof(buf)));
760 else
761 return random_state_of_last_resort ();
762 }
763 #undef FUNC_NAME
764
765 void
766 scm_init_random ()
767 {
768 int i, m;
769 /* plug in default RNG */
770 scm_t_rng rng =
771 {
772 sizeof (scm_t_i_rstate),
773 scm_i_uniform32,
774 scm_i_init_rstate,
775 scm_i_copy_rstate,
776 scm_i_rstate_from_datum,
777 scm_i_rstate_to_datum
778 };
779 scm_the_rng = rng;
780
781 scm_tc16_rstate = scm_make_smob_type ("random-state", 0);
782
783 for (m = 1; m <= 0x100; m <<= 1)
784 for (i = m >> 1; i < m; ++i)
785 scm_masktab[i] = m - 1;
786
787 #include "libguile/random.x"
788
789 scm_add_feature ("random");
790 }
791
792 /*
793 Local Variables:
794 c-file-style: "gnu"
795 End:
796 */