cae3f9e70a3fb9f0f05411bfa3114509d9fd768c
[pspp-builds.git] / src / random.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "random.h"
22 #include <assert.h>
23 #include <inttypes.h>
24 #include <limits.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <time.h>
28 #include "alloc.h"
29 #include "magic.h"
30 #include "settings.h"
31
32 /* Random number generator. */
33 struct rng 
34   {
35     /* RC4-based random bytes. */
36     uint8_t s[256];
37     uint8_t i, j;
38
39     /* Normal distribution. */
40     double next_normal;
41   };
42
43 /* Creates a new random number generator, seeds it based on
44    the current time, and returns it. */
45 struct rng *
46 rng_create (void) 
47 {
48   struct rng *rng;
49   static time_t t=0;
50
51   rng = xmalloc (sizeof *rng);
52   if (t == 0 || set_seed_used)
53   {
54     if (set_seed == NOT_LONG) 
55       time (&t);
56     else
57       t = set_seed;
58     set_seed_used=0;
59   }
60   else
61     t++;
62   rng_seed (rng, &t, sizeof t);
63   rng->next_normal = NOT_DOUBLE;
64   return rng;
65 }
66
67 /* Destroys RNG. */
68 void
69 rng_destroy (struct rng *rng) 
70 {
71   free (rng);
72 }
73
74 /* Swap bytes. */
75 static void
76 swap_byte (uint8_t *a, uint8_t *b) 
77 {
78   uint8_t t = *a;
79   *a = *b;
80   *b = t;
81 }
82
83 /* Seeds RNG based on the SIZE bytes in BUF.
84    At most the first 256 bytes of BUF are used. */
85 void
86 rng_seed (struct rng *rng, const void *key_, size_t size) 
87 {
88   const uint8_t *key = key_;
89   size_t key_idx;
90   uint8_t *s;
91   int i, j;
92
93   assert (rng != NULL);
94
95   s = rng->s;
96   rng->i = rng->j = 0;
97   for (i = 0; i < 256; i++) 
98     s[i] = i;
99   for (key_idx = 0, i = j = 0; i < 256; i++) 
100     {
101       j = (j + s[i] + key[key_idx]) & 255;
102       swap_byte (s + i, s + j);
103       if (++key_idx >= size)
104         key_idx = 0;
105     }
106 }
107
108 /* Reads SIZE random bytes from RNG into BUF. */
109 void
110 rng_get_bytes (struct rng *rng, void *buf_, size_t size) 
111 {
112   uint8_t *buf = buf_;
113   uint8_t *s;
114   uint8_t i, j;
115
116   assert (rng != 0);
117
118   s = rng->s;
119   i = rng->i;
120   j = rng->j;
121   while (size-- > 0) 
122     {
123       i += 1;
124       j += s[i];
125       swap_byte (s + i, s + j);
126       *buf++ = s[(s[i] + s[j]) & 255];
127     }
128   rng->i = i;
129   rng->j = j;
130 }
131
132 /* Returns a random int in the range [0, INT_MAX]. */
133 int
134 rng_get_int (struct rng *rng) 
135 {
136   int value;
137
138   do 
139     {
140       rng_get_bytes (rng, &value, sizeof value);
141       value = abs (value);
142     }
143   while (value < 0);
144    
145   return value;
146 }
147
148 /* Returns a random unsigned in the range [0, UINT_MAX]. */
149 unsigned
150 rng_get_unsigned (struct rng *rng) 
151 {
152   unsigned value;
153
154   rng_get_bytes (rng, &value, sizeof value);
155   return value;
156 }
157
158 /* Returns a random number from the uniform distribution with
159    range [0,1). */
160 double
161 rng_get_double (struct rng *rng) 
162 {
163   for (;;) 
164     {
165       unsigned long ulng;
166       double dbl;
167   
168       rng_get_bytes (rng, &ulng, sizeof ulng);
169       dbl = ulng / (ULONG_MAX + 1.0);
170       if (dbl >= 0 && dbl < 1)
171         return dbl;
172     }
173 }
174
175 /* Returns a random number from the distribution with mean 0 and
176    standard deviation 1.  (Multiply the result by the desired
177    standard deviation, then add the desired mean.) */
178 double 
179 rng_get_double_normal (struct rng *rng)
180 {
181   /* Knuth, _The Art of Computer Programming_, Vol. 2, 3.4.1C,
182      Algorithm P. */
183   double this_normal;
184   
185   if (rng->next_normal != NOT_DOUBLE)
186     {
187       this_normal = rng->next_normal;
188       rng->next_normal = NOT_DOUBLE;
189     }
190   else 
191     {
192       double v1, v2, s;
193       
194       do
195         {
196           double u1 = rng_get_double (rng);
197           double u2 = rng_get_double (rng);
198           v1 = 2.0 * u1 - 1.0;
199           v2 = 2.0 * u2 - 1.0;
200           s = v1 * v1 + v2 * v2;
201         }
202       while (s >= 1);
203
204       this_normal = v1 * sqrt (-2. * log (s) / s);
205       rng->next_normal = v2 * sqrt (-2. * log (s) / s); 
206     }
207   
208   return this_normal;
209 }
210
211 /* Gets an initialized RNG for use in PSPP transformations and
212    procedures. */
213 struct rng *
214 pspp_rng (void)
215 {
216   static struct rng *rng;
217
218   if (rng == NULL)
219     rng = rng_create ();
220   return rng;
221 }