Tests for array-copy!, empty case
[bpt/guile.git] / libguile / random.c
CommitLineData
be6a36a0
MW
1/* Copyright (C) 1999, 2000, 2001, 2003, 2005, 2006, 2009, 2010,
2 * 2012, 2013 Free Software Foundation, Inc.
73be1d9e 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
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.
e7a72986 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
e7a72986 12 *
73be1d9e
MV
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
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
73be1d9e 17 */
e7a72986 18
1bbd0b84
GB
19
20
587f4edd 21/* Original Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
e7a72986 22
dbb605f5 23#ifdef HAVE_CONFIG_H
2fb8013c
RB
24# include <config.h>
25#endif
26
a0599745 27#include "libguile/_scm.h"
e7a72986 28
8db9cc6c 29#include <gmp.h>
7f146094 30#include <stdio.h>
e7a72986 31#include <math.h>
f34d19c7 32#include <string.h>
587f4edd
MW
33#include <sys/types.h>
34
35#ifdef HAVE_UNISTD_H
36#include <unistd.h>
37#endif
38
a0599745
MD
39#include "libguile/smob.h"
40#include "libguile/numbers.h"
41#include "libguile/feature.h"
42#include "libguile/strings.h"
2fa901a5 43#include "libguile/arrays.h"
46d25cff 44#include "libguile/srfi-4.h"
a0599745 45#include "libguile/vectors.h"
f332e957 46#include "libguile/generalized-vectors.h"
e7a72986 47
a0599745
MD
48#include "libguile/validate.h"
49#include "libguile/random.h"
e7a72986
MD
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
92c2555f 65scm_t_rng scm_the_rng;
e7a72986
MD
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
99a0ee66
AW
81typedef 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
e7a72986
MD
88#define A 2131995753UL
89
82893676
MG
90#ifndef M_PI
91#define M_PI 3.14159265359
92#endif
93
99a0ee66
AW
94static scm_t_uint32
95scm_i_uniform32 (scm_t_rstate *state)
e7a72986 96{
99a0ee66
AW
97 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
98 scm_t_uint64 x = (scm_t_uint64) A * istate->w + istate->c;
4a9f83ff 99 scm_t_uint32 w = x & 0xffffffffUL;
99a0ee66
AW
100 istate->w = w;
101 istate->c = x >> 32L;
e7a72986
MD
102 return w;
103}
104
99a0ee66
AW
105static void
106scm_i_init_rstate (scm_t_rstate *state, const char *seed, int n)
e7a72986 107{
99a0ee66 108 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
4a9f83ff
MD
109 scm_t_uint32 w = 0L;
110 scm_t_uint32 c = 0L;
e7a72986
MD
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 }
8b3747f9 120 if ((w == 0 && c == 0) || (w == -1 && c == A - 1))
e7a72986 121 ++c;
99a0ee66
AW
122 istate->w = w;
123 istate->c = c;
e7a72986
MD
124}
125
99a0ee66
AW
126static scm_t_rstate *
127scm_i_copy_rstate (scm_t_rstate *state)
e7a72986 128{
92d8fd32
LC
129 scm_t_rstate *new_state;
130
a2a95453 131 new_state = scm_gc_malloc_pointerless (state->rng->rstate_size,
92d8fd32 132 "random-state");
a2a95453 133 return memcpy (new_state, state, state->rng->rstate_size);
e7a72986
MD
134}
135
77b13912
AR
136SCM_SYMBOL(scm_i_rstate_tag, "multiply-with-carry");
137
99a0ee66
AW
138static void
139scm_i_rstate_from_datum (scm_t_rstate *state, SCM value)
140#define FUNC_NAME "scm_i_rstate_from_datum"
77b13912 141{
99a0ee66 142 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
b606ff6a 143 scm_t_uint32 w, c;
77b13912
AR
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);
b606ff6a
AW
150 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADR (value), w);
151 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADDR (value), c);
77b13912 152
99a0ee66
AW
153 istate->w = w;
154 istate->c = c;
77b13912
AR
155}
156#undef FUNC_NAME
157
99a0ee66
AW
158static SCM
159scm_i_rstate_to_datum (scm_t_rstate *state)
77b13912 160{
99a0ee66 161 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
77b13912 162 return scm_list_3 (scm_i_rstate_tag,
99a0ee66
AW
163 scm_from_uint32 (istate->w),
164 scm_from_uint32 (istate->c));
77b13912
AR
165}
166
e7a72986
MD
167\f
168/*
169 * Random number library functions
170 */
171
92c2555f 172scm_t_rstate *
cc95e00a 173scm_c_make_rstate (const char *seed, int n)
5ee11b7c 174{
92d8fd32
LC
175 scm_t_rstate *state;
176
177 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
178 "random-state");
a2a95453
AW
179 state->rng = &scm_the_rng;
180 state->normal_next = 0.0;
181 state->rng->init_rstate (state, seed, n);
5ee11b7c
MD
182 return state;
183}
184
77b13912 185scm_t_rstate *
99a0ee66 186scm_c_rstate_from_datum (SCM datum)
77b13912
AR
187{
188 scm_t_rstate *state;
189
190 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
191 "random-state");
a2a95453
AW
192 state->rng = &scm_the_rng;
193 state->normal_next = 0.0;
194 state->rng->from_datum (state, datum);
77b13912
AR
195 return state;
196}
2ade72d7 197
92c2555f 198scm_t_rstate *
9b741bb6 199scm_c_default_rstate ()
2ade72d7 200#define FUNC_NAME "scm_c_default_rstate"
9b741bb6 201{
c5f268f8 202 SCM state = SCM_VARIABLE_REF (scm_var_random_state);
2ade72d7
DH
203 if (!SCM_RSTATEP (state))
204 SCM_MISC_ERROR ("*random-state* contains bogus random state", SCM_EOL);
9b741bb6
MD
205 return SCM_RSTATE (state);
206}
2ade72d7
DH
207#undef FUNC_NAME
208
9b741bb6 209
a2a95453 210double
92c2555f 211scm_c_uniform01 (scm_t_rstate *state)
e7a72986 212{
a2a95453
AW
213 double x = (double) state->rng->random_bits (state) / (double) 0xffffffffUL;
214 return ((x + (double) state->rng->random_bits (state))
5a92ddfd 215 / (double) 0xffffffffUL);
e7a72986
MD
216}
217
218double
92c2555f 219scm_c_normal01 (scm_t_rstate *state)
e7a72986 220{
a2a95453 221 if (state->normal_next != 0.0)
e7a72986 222 {
a2a95453
AW
223 double ret = state->normal_next;
224
225 state->normal_next = 0.0;
226
227 return ret;
e7a72986
MD
228 }
229 else
230 {
231 double r, a, n;
e7a72986 232
9b741bb6
MD
233 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
234 a = 2.0 * M_PI * scm_c_uniform01 (state);
e7a72986
MD
235
236 n = r * sin (a);
a2a95453 237 state->normal_next = r * cos (a);
e7a72986
MD
238
239 return n;
240 }
241}
242
243double
92c2555f 244scm_c_exp1 (scm_t_rstate *state)
e7a72986 245{
9b741bb6 246 return - log (scm_c_uniform01 (state));
e7a72986
MD
247}
248
249unsigned char scm_masktab[256];
250
2af6e135
AR
251static inline scm_t_uint32
252scm_i_mask32 (scm_t_uint32 m)
e7a72986 253{
2af6e135 254 return (m < 0x100
e7a72986
MD
255 ? scm_masktab[m]
256 : (m < 0x10000
5a92ddfd 257 ? scm_masktab[m >> 8] << 8 | 0xff
e7a72986 258 : (m < 0x1000000
5a92ddfd
MD
259 ? scm_masktab[m >> 16] << 16 | 0xffff
260 : scm_masktab[m >> 24] << 24 | 0xffffff)));
2af6e135
AR
261}
262
263scm_t_uint32
264scm_c_random (scm_t_rstate *state, scm_t_uint32 m)
265{
266 scm_t_uint32 r, mask = scm_i_mask32 (m);
a2a95453 267 while ((r = state->rng->random_bits (state) & mask) >= m);
e7a72986
MD
268 return r;
269}
270
2af6e135
AR
271scm_t_uint64
272scm_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
ea7f6344
MD
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
e7a72986 302SCM
92c2555f 303scm_c_random_bignum (scm_t_rstate *state, SCM m)
e7a72986 304{
969d3bd0 305 SCM result = scm_i_mkbig ();
969d3bd0 306 const size_t m_bits = mpz_sizeinbase (SCM_I_BIG_MPZ (m), 2);
b606ff6a
AW
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);
969d3bd0
RB
313
314 /* we know the result will be this big */
315 mpz_realloc2 (SCM_I_BIG_MPZ (result), m_bits);
316
317 random_chunks =
b606ff6a 318 (scm_t_uint32 *) scm_gc_calloc (num_chunks * sizeof (scm_t_uint32),
969d3bd0
RB
319 "random bignum chunks");
320
372691d8 321 do
e7a72986 322 {
b606ff6a
AW
323 scm_t_uint32 *current_chunk = random_chunks + (num_chunks - 1);
324 scm_t_uint32 chunks_left = num_chunks;
969d3bd0
RB
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 */
a2a95453 332 const scm_t_uint32 rndbits = state->rng->random_bits (state);
b606ff6a
AW
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;
969d3bd0
RB
336 *current_chunk-- = highest_bits;
337 chunks_left--;
338 }
339
340 while (chunks_left)
341 {
b606ff6a 342 /* now fill in the remaining scm_t_uint32 sized chunks */
a2a95453 343 *current_chunk-- = state->rng->random_bits (state);
969d3bd0
RB
344 chunks_left--;
345 }
346 mpz_import (SCM_I_BIG_MPZ (result),
347 num_chunks,
348 -1,
b606ff6a 349 sizeof (scm_t_uint32),
969d3bd0
RB
350 0,
351 0,
352 random_chunks);
372691d8
MD
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);
969d3bd0 356 scm_gc_free (random_chunks,
b606ff6a 357 num_chunks * sizeof (scm_t_uint32),
969d3bd0 358 "random bignum chunks");
8db9cc6c 359 return scm_i_normbig (result);
e7a72986
MD
360}
361
362/*
363 * Scheme level representation of random states.
364 */
365
92c2555f 366scm_t_bits scm_tc16_rstate;
e7a72986
MD
367
368static SCM
92c2555f 369make_rstate (scm_t_rstate *state)
e7a72986 370{
23a62151 371 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
e7a72986
MD
372}
373
e7a72986 374
e7a72986
MD
375/*
376 * Scheme level interface.
377 */
378
cc95e00a 379SCM_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")));
e7a72986 380
a1ec6916 381SCM_DEFINE (scm_random, "random", 1, 1, 0,
1bbd0b84 382 (SCM n, SCM state),
34d19ef6 383 "Return a number in [0, N).\n"
d928e0b4 384 "\n"
9401323e
NJ
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"
d928e0b4
GB
388 "distribution.\n"
389 "\n"
3b644514
MG
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.")
1bbd0b84 395#define FUNC_NAME s_scm_random
e7a72986
MD
396{
397 if (SCM_UNBNDP (state))
86d31dfe 398 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 399 SCM_VALIDATE_RSTATE (2, state);
e11e83f3 400 if (SCM_I_INUMP (n))
e7a72986 401 {
e25f3727 402 scm_t_bits m = (scm_t_bits) SCM_I_INUM (n);
9defb641 403 SCM_ASSERT_RANGE (1, n, SCM_I_INUM (n) > 0);
e25f3727 404#if SCM_SIZEOF_UINTPTR_T <= 4
9defb641
AW
405 return scm_from_uint32 (scm_c_random (SCM_RSTATE (state),
406 (scm_t_uint32) m));
e25f3727 407#elif SCM_SIZEOF_UINTPTR_T <= 8
2af6e135
AR
408 return scm_from_uint64 (scm_c_random64 (SCM_RSTATE (state),
409 (scm_t_uint64) m));
9defb641 410#else
e25f3727 411#error "Cannot deal with this platform's scm_t_bits size"
9defb641 412#endif
e7a72986 413 }
34d19ef6 414 SCM_VALIDATE_NIM (1, n);
e7a72986 415 if (SCM_REALP (n))
d9a67fc4
MV
416 return scm_from_double (SCM_REAL_VALUE (n)
417 * scm_c_uniform01 (SCM_RSTATE (state)));
969d3bd0 418
a55c2b68
MV
419 if (!SCM_BIGP (n))
420 SCM_WRONG_TYPE_ARG (1, n);
9b741bb6 421 return scm_c_random_bignum (SCM_RSTATE (state), n);
e7a72986 422}
1bbd0b84 423#undef FUNC_NAME
e7a72986 424
a1ec6916 425SCM_DEFINE (scm_copy_random_state, "copy-random-state", 0, 1, 0,
1bbd0b84 426 (SCM state),
3b644514 427 "Return a copy of the random state @var{state}.")
1bbd0b84 428#define FUNC_NAME s_scm_copy_random_state
e7a72986
MD
429{
430 if (SCM_UNBNDP (state))
86d31dfe 431 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 432 SCM_VALIDATE_RSTATE (1, state);
a2a95453 433 return make_rstate (SCM_RSTATE (state)->rng->copy_rstate (SCM_RSTATE (state)));
e7a72986 434}
1bbd0b84 435#undef FUNC_NAME
e7a72986 436
a1ec6916 437SCM_DEFINE (scm_seed_to_random_state, "seed->random-state", 1, 0, 0,
1bbd0b84 438 (SCM seed),
3b644514 439 "Return a new random state using @var{seed}.")
1bbd0b84 440#define FUNC_NAME s_scm_seed_to_random_state
5ee11b7c 441{
8824ac88 442 SCM res;
5ee11b7c
MD
443 if (SCM_NUMBERP (seed))
444 seed = scm_number_to_string (seed, SCM_UNDEFINED);
34d19ef6 445 SCM_VALIDATE_STRING (1, seed);
cc95e00a
MV
446 res = make_rstate (scm_c_make_rstate (scm_i_string_chars (seed),
447 scm_i_string_length (seed)));
8824ac88
MV
448 scm_remember_upto_here_1 (seed);
449 return res;
450
5ee11b7c 451}
1bbd0b84 452#undef FUNC_NAME
5ee11b7c 453
99a0ee66
AW
454SCM_DEFINE (scm_datum_to_random_state, "datum->random-state", 1, 0, 0,
455 (SCM datum),
1d454874 456 "Return a new random state using @var{datum}, which should have\n"
ffb62a43 457 "been obtained from @code{random-state->datum}.")
99a0ee66 458#define FUNC_NAME s_scm_datum_to_random_state
77b13912 459{
99a0ee66 460 return make_rstate (scm_c_rstate_from_datum (datum));
77b13912
AR
461}
462#undef FUNC_NAME
463
99a0ee66 464SCM_DEFINE (scm_random_state_to_datum, "random-state->datum", 1, 0, 0,
77b13912 465 (SCM state),
99a0ee66
AW
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
77b13912
AR
469{
470 SCM_VALIDATE_RSTATE (1, state);
a2a95453 471 return SCM_RSTATE (state)->rng->to_datum (SCM_RSTATE (state));
77b13912
AR
472}
473#undef FUNC_NAME
474
a1ec6916 475SCM_DEFINE (scm_random_uniform, "random:uniform", 0, 1, 0,
1bbd0b84 476 (SCM state),
1e6808ea
MG
477 "Return a uniformly distributed inexact real random number in\n"
478 "[0,1).")
1bbd0b84 479#define FUNC_NAME s_scm_random_uniform
e7a72986
MD
480{
481 if (SCM_UNBNDP (state))
86d31dfe 482 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 483 SCM_VALIDATE_RSTATE (1, state);
d9a67fc4 484 return scm_from_double (scm_c_uniform01 (SCM_RSTATE (state)));
e7a72986 485}
1bbd0b84 486#undef FUNC_NAME
e7a72986 487
a1ec6916 488SCM_DEFINE (scm_random_normal, "random:normal", 0, 1, 0,
1bbd0b84 489 (SCM state),
1e6808ea
MG
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)))}.")
1bbd0b84 494#define FUNC_NAME s_scm_random_normal
afe5177e
GH
495{
496 if (SCM_UNBNDP (state))
86d31dfe 497 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 498 SCM_VALIDATE_RSTATE (1, state);
d9a67fc4 499 return scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
afe5177e 500}
1bbd0b84 501#undef FUNC_NAME
afe5177e 502
e7a72986 503static void
46d25cff 504vector_scale_x (SCM v, double c)
e7a72986 505{
46d25cff 506 size_t n;
4057a3e0 507 if (scm_is_simple_vector (v))
46d25cff 508 {
4057a3e0 509 n = SCM_SIMPLE_VECTOR_LENGTH (v);
46d25cff 510 while (n-- > 0)
4057a3e0 511 SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n)) *= c;
46d25cff 512 }
e7a72986 513 else
46d25cff
MV
514 {
515 /* must be a f64vector. */
4057a3e0
MV
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;
c8857a4d
MV
525
526 scm_array_handle_release (&handle);
46d25cff 527 }
e7a72986
MD
528}
529
530static double
531vector_sum_squares (SCM v)
532{
533 double x, sum = 0.0;
46d25cff 534 size_t n;
4057a3e0 535 if (scm_is_simple_vector (v))
46d25cff 536 {
4057a3e0 537 n = SCM_SIMPLE_VECTOR_LENGTH (v);
46d25cff
MV
538 while (n-- > 0)
539 {
4057a3e0 540 x = SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n));
46d25cff
MV
541 sum += x * x;
542 }
543 }
e7a72986 544 else
46d25cff
MV
545 {
546 /* must be a f64vector. */
4057a3e0
MV
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)
46d25cff 555 {
4057a3e0 556 x = *elts;
46d25cff
MV
557 sum += x * x;
558 }
4057a3e0 559
c8857a4d 560 scm_array_handle_release (&handle);
46d25cff 561 }
e7a72986
MD
562 return sum;
563}
564
e7a72986
MD
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 */
a1ec6916 570SCM_DEFINE (scm_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0,
1bbd0b84 571 (SCM v, SCM state),
6efaeb35
KR
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.")
1bbd0b84 577#define FUNC_NAME s_scm_random_solid_sphere_x
e7a72986 578{
e7a72986 579 if (SCM_UNBNDP (state))
86d31dfe 580 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 581 SCM_VALIDATE_RSTATE (2, state);
e7a72986 582 scm_random_normal_vector_x (v, state);
46d25cff
MV
583 vector_scale_x (v,
584 pow (scm_c_uniform01 (SCM_RSTATE (state)),
f160e709 585 1.0 / scm_c_generalized_vector_length (v))
46d25cff 586 / sqrt (vector_sum_squares (v)));
e7a72986
MD
587 return SCM_UNSPECIFIED;
588}
1bbd0b84 589#undef FUNC_NAME
e7a72986 590
a1ec6916 591SCM_DEFINE (scm_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0,
1bbd0b84 592 (SCM v, SCM state),
d928e0b4
GB
593 "Fills vect with inexact real random numbers\n"
594 "the sum of whose squares is equal to 1.0.\n"
9401323e 595 "Thinking of vect as coordinates in space of\n"
d928e0b4 596 "dimension n = (vector-length vect), the coordinates\n"
9401323e 597 "are uniformly distributed over the surface of the\n"
72dd0a03 598 "unit n-sphere.")
1bbd0b84 599#define FUNC_NAME s_scm_random_hollow_sphere_x
e7a72986 600{
e7a72986 601 if (SCM_UNBNDP (state))
86d31dfe 602 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 603 SCM_VALIDATE_RSTATE (2, state);
e7a72986 604 scm_random_normal_vector_x (v, state);
46d25cff 605 vector_scale_x (v, 1 / sqrt (vector_sum_squares (v)));
e7a72986
MD
606 return SCM_UNSPECIFIED;
607}
1bbd0b84 608#undef FUNC_NAME
e7a72986 609
1bbd0b84 610
a1ec6916 611SCM_DEFINE (scm_random_normal_vector_x, "random:normal-vector!", 1, 1, 0,
1bbd0b84 612 (SCM v, SCM state),
d928e0b4
GB
613 "Fills vect with inexact real random numbers that are\n"
614 "independent and standard normally distributed\n"
64ba8e85 615 "(i.e., with mean 0 and variance 1).")
1bbd0b84 616#define FUNC_NAME s_scm_random_normal_vector_x
e7a72986 617{
4057a3e0
MV
618 long i;
619 scm_t_array_handle handle;
620 scm_t_array_dim *dim;
46d25cff 621
e7a72986 622 if (SCM_UNBNDP (state))
86d31dfe 623 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 624 SCM_VALIDATE_RSTATE (2, state);
4057a3e0 625
c8857a4d 626 scm_generalized_vector_get_handle (v, &handle);
4057a3e0
MV
627 dim = scm_array_handle_dims (&handle);
628
629 if (scm_is_vector (v))
46d25cff 630 {
4057a3e0
MV
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)));
46d25cff 634 }
e7a72986 635 else
46d25cff
MV
636 {
637 /* must be a f64vector. */
4057a3e0
MV
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));
46d25cff 641 }
4057a3e0 642
c8857a4d
MV
643 scm_array_handle_release (&handle);
644
e7a72986
MD
645 return SCM_UNSPECIFIED;
646}
1bbd0b84 647#undef FUNC_NAME
e7a72986 648
a1ec6916 649SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
1bbd0b84 650 (SCM state),
1e6808ea
MG
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)).")
1bbd0b84 654#define FUNC_NAME s_scm_random_exp
e7a72986
MD
655{
656 if (SCM_UNBNDP (state))
86d31dfe 657 state = SCM_VARIABLE_REF (scm_var_random_state);
34d19ef6 658 SCM_VALIDATE_RSTATE (1, state);
d9a67fc4 659 return scm_from_double (scm_c_exp1 (SCM_RSTATE (state)));
e7a72986 660}
1bbd0b84 661#undef FUNC_NAME
e7a72986 662
444b26f7
MW
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. */
d47db067
MW
668static SCM
669random_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 */
587f4edd
MW
675 /* Avoid scm_getpid, since it depends on HAVE_POSIX. */
676 scm_from_unsigned_integer (getpid ()), /* process ID */
d47db067
MW
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);
444b26f7 682
d47db067 683 /* Concatenate the sources bitwise to form the seed */
3ec19a78 684 SCM seed = SCM_INUM0;
d47db067
MW
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. */
719static int
720read_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. */
736void
737scm_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
751SCM_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\
754source 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
e7a72986
MD
765void
766scm_init_random ()
767{
768 int i, m;
769 /* plug in default RNG */
92c2555f 770 scm_t_rng rng =
e7a72986 771 {
92c2555f 772 sizeof (scm_t_i_rstate),
99a0ee66
AW
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
e7a72986
MD
778 };
779 scm_the_rng = rng;
780
e841c3e0 781 scm_tc16_rstate = scm_make_smob_type ("random-state", 0);
e7a72986
MD
782
783 for (m = 1; m <= 0x100; m <<= 1)
784 for (i = m >> 1; i < m; ++i)
785 scm_masktab[i] = m - 1;
786
a0599745 787#include "libguile/random.x"
e7a72986
MD
788
789 scm_add_feature ("random");
790}
89e00824
ML
791
792/*
793 Local Variables:
794 c-file-style: "gnu"
795 End:
796*/