1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2008. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Initial release: May 2008
8 
9         author:         Various
10 
11         Since:          0.99.7
12 
13         With gratitude to Dr Jurgen A Doornik. See his paper entitled
14         "Conversion of high-period random numbers to floating point"
15         
16 *******************************************************************************/
17 
18 module tango.math.random.Kiss;
19 
20 
21 version (Win32)
22          private extern(Windows) int QueryPerformanceCounter (ulong *);
23 
24 version (Posix)
25         {
26         private import tango.stdc.posix.sys.time;
27         }
28 
29 
30 /******************************************************************************
31 
32         KISS (from George Marsaglia)
33 
34         The idea is to use simple, fast, individually promising
35         generators to get a composite that will be fast, easy to code
36         have a very long period and pass all the tests put to it.
37         The three components of KISS are
38         ---
39                 x(n)=a*x(n-1)+1 mod 2^32
40                 y(n)=y(n-1)(I+L^13)(I+R^17)(I+L^5),
41                 z(n)=2*z(n-1)+z(n-2) +carry mod 2^32
42         ---
43 
44         The y's are a shift register sequence on 32bit binary vectors
45         period 2^32-1; The z's are a simple multiply-with-carry sequence
46         with period 2^63+2^32-1. The period of KISS is thus
47         ---
48                 2^32*(2^32-1)*(2^63+2^32-1) > 2^127
49         ---
50 
51         Note that this should be passed by reference, unless you really
52         intend to provide a local copy to a callee
53         
54 ******************************************************************************/
55 
56 struct Kiss
57 {
58         ///
59         public alias natural  toInt;
60         ///
61         public alias fraction toReal;
62         
63         private uint kiss_k;
64         private uint kiss_m;
65         private uint kiss_x = 1;
66         private uint kiss_y = 2;
67         private uint kiss_z = 4;
68         private uint kiss_w = 8;
69         private uint kiss_carry = 0;
70         
71         private enum double M_RAN_INVM32 = 2.32830643653869628906e-010,
72                             M_RAN_INVM52 = 2.22044604925031308085e-016;
73       
74         /**********************************************************************
75 
76                 A global, shared instance, seeded via startup time
77 
78         **********************************************************************/
79 
80         public static __gshared Kiss instance; 
81 
82         shared static this ()
83         {
84                 instance.seed();
85         }
86 
87         /**********************************************************************
88 
89                 Creates and seeds a new generator with the current time
90 
91         **********************************************************************/
92 
93         static Kiss opCall ()
94         {
95                 Kiss rand;
96                 rand.seed();
97                 return rand;
98         }
99 
100         /**********************************************************************
101 
102                 Seed the generator with current time
103 
104         **********************************************************************/
105 
106         void seed ()
107         {
108                 ulong s;
109 
110                 version (Posix)
111                         {
112                         timeval tv;
113 
114                         gettimeofday (&tv, null);
115                         s = tv.tv_usec;
116                         }
117                 version (Win32)
118                          QueryPerformanceCounter (&s);
119 
120                 return seed (cast(uint) s);
121         }
122 
123         /**********************************************************************
124 
125                 Seed the generator with a provided value
126 
127         **********************************************************************/
128 
129         void seed (uint seed)
130         {
131                 kiss_x = seed | 1;
132                 kiss_y = seed | 2;
133                 kiss_z = seed | 4;
134                 kiss_w = seed | 8;
135                 kiss_carry = 0;
136         }
137 
138         /**********************************************************************
139 
140                 Returns X such that 0 <= X <= uint.max
141 
142         **********************************************************************/
143 
144         uint natural ()
145         {
146                 kiss_x = kiss_x * 69069 + 1;
147                 kiss_y ^= kiss_y << 13;
148                 kiss_y ^= kiss_y >> 17;
149                 kiss_y ^= kiss_y << 5;
150                 kiss_k = (kiss_z >> 2) + (kiss_w >> 3) + (kiss_carry >> 2);
151                 kiss_m = kiss_w + kiss_w + kiss_z + kiss_carry;
152                 kiss_z = kiss_w;
153                 kiss_w = kiss_m;
154                 kiss_carry = kiss_k >> 30;
155                 return kiss_x + kiss_y + kiss_w;
156         }
157 
158         /**********************************************************************
159 
160                 Returns X such that 0 <= X < max
161 
162                 Note that max is exclusive, making it compatible with
163                 array indexing
164 
165         **********************************************************************/
166 
167         uint natural (uint max)
168         {
169                 return natural() % max;
170         }
171 
172         /**********************************************************************
173 
174                 Returns X such that min <= X < max
175 
176                 Note that max is exclusive, making it compatible with
177                 array indexing
178 
179         **********************************************************************/
180 
181         uint natural (uint min, uint max)
182         {
183                 return (natural() % (max-min)) + min;
184         }
185         
186         /**********************************************************************
187         
188                 Returns a value in the range [0, 1) using 32 bits
189                 of precision (with thanks to Dr Jurgen A Doornik)
190 
191         **********************************************************************/
192 
193         double fraction ()
194         {
195                 return ((cast(int) natural()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM32 / 2));
196         }
197 
198         /**********************************************************************
199 
200                 Returns a value in the range [0, 1) using 52 bits
201                 of precision (with thanks to Dr Jurgen A Doornik)
202 
203         **********************************************************************/
204 
205         double fractionEx ()
206         {
207                 return ((cast(int) natural()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM52 / 2) + 
208                        ((cast(int) natural()) & 0x000FFFFF) * M_RAN_INVM52);
209         }
210 }
211 
212 
213 
214 /******************************************************************************
215 
216 
217 ******************************************************************************/
218 
219 debug (Kiss)
220 {
221         import tango.io.Stdout;
222         import tango.time.StopWatch;
223 
224         void main()
225         {
226                 auto dbl = Kiss();
227                 auto count = 100_000_000;
228                 StopWatch w;
229 
230                 w.start;
231                 double v1;
232                 for (int i=count; --i;)
233                      v1 = dbl.fraction;
234                 Stdout.formatln ("{} fraction, {}/s, {:f10}", count, count/w.stop, v1);
235 
236                 w.start;
237                 for (int i=count; --i;)
238                      v1 = dbl.fractionEx;
239                 Stdout.formatln ("{} fractionEx, {}/s, {:f10}", count, count/w.stop, v1);
240 
241                 for (int i=count; --i;)
242                     {
243                     auto v = dbl.fraction;
244                     if (v <= 0.0 || v >= 1.0)
245                        {
246                        Stdout.formatln ("fraction {:f10}", v);
247                        break;
248                        }
249                     v = dbl.fractionEx;
250                     if (v <= 0.0 || v >= 1.0)
251                        {
252                        Stdout.formatln ("fractionEx {:f10}", v);
253                        break;
254                        }
255                     }
256         }
257 }