1 /*******************************************************************************
2         copyright:      Copyright (c) 2008. Fawzi Mohamed
3         license:        BSD style: $(LICENSE)
4         version:        Initial release: July 2008
5         author:         Fawzi Mohamed
6 *******************************************************************************/
7 module tango.math.random.engines.CMWC;
8 private import Integer=tango.text.convert.Integer;
9 
10 /+ CMWC a random number generator,
11 + Marisaglia, Journal of Modern Applied Statistical Methods (2003), vol.2,No.1,p 2-13
12 + a simple and fast RNG that passes all statistical tests, has a large seed, and is very fast
13 + By default ComplimentaryMultiplyWithCarry with r=1024, a=987769338, b=2^32-1, period a*b^r>10^9873
14 + This is the engine, *never* use it directly, always use it though a RandomG class
15 +/
16 struct CMWC(uint cmwc_r=1024U,ulong cmwc_a=987769338UL){
17     uint[cmwc_r] cmwc_q=void;
18     uint cmwc_i=cmwc_r-1u;
19     uint cmwc_c=123;
20     uint nBytes = 0;
21     uint restB  = 0;
22 
23     enum int canCheckpoint=true;
24     enum int canSeed=true;
25     
26     void skip(uint n){
27         for (int i=n;i!=n;--i){
28             next();
29         }
30     }
31     ubyte nextB(){
32         if (nBytes>0) {
33             ubyte res=cast(ubyte)(restB & 0xFF);
34             restB >>= 8;
35             --nBytes;
36             return res;
37         } else {
38             restB=next();
39             ubyte res=cast(ubyte)(restB & 0xFF);
40             restB >>= 8;
41             nBytes=3;
42             return res;
43         }
44     }
45     uint next(){
46         enum uint rMask=cmwc_r-1u;
47         static assert((rMask&cmwc_r)==0,"cmwc_r is supposed to be a power of 2"); // put a more stringent test?
48         enum uint m=0xFFFF_FFFE;
49         cmwc_i=(cmwc_i+1)&rMask;
50         ulong t=cmwc_a*cmwc_q[cmwc_i]+cmwc_c;
51         cmwc_c=cast(uint)(t>>32);
52         uint x=cast(uint)t+cmwc_c;
53         if (x<cmwc_c) {
54             ++x; ++cmwc_c;
55         }
56         return (cmwc_q[cmwc_i]=m-x);
57     }
58     
59     ulong nextL(){
60         return ((cast(ulong)next())<<32)+cast(ulong)next();
61     }
62     
63     void seed(scope uint delegate() rSeed){
64         cmwc_i=cmwc_r-1u; // randomize also this?
65         for (int ii=0;ii<10;++ii){
66             for (uint i=0;i<cmwc_r;++i){
67                 cmwc_q[i]=rSeed();
68             }
69             uint nV=rSeed();
70             uint to=(uint.max/cmwc_a)*cmwc_a;
71             for (int i=0;i<10;++i){
72                 if (nV<to) break;
73                 nV=rSeed();
74             }
75             cmwc_c=cast(uint)(nV%cmwc_a);
76             nBytes = 0;
77             restB=0;
78             if (cmwc_c==0){
79                 for (uint i=0;i<cmwc_r;++i)
80                     if (cmwc_q[i]!=0) return;
81             } else if (cmwc_c==cmwc_a-1){
82                 for (uint i=0;i<cmwc_r;++i)
83                     if (cmwc_q[i]!=0xFFFF_FFFF) return;
84             } else return;
85         }
86         cmwc_c=1;
87     }
88     /// writes the current status in a string
89     immutable(char)[] toString(){
90         char[] res=new char[4+16+(cmwc_r+5)*9];
91         int i=0;
92         res[i..i+4]="CMWC"[];
93         i+=4;
94         Integer.format(res[i..i+16],cmwc_a,cast(char[])"x16");
95         i+=16;
96         res[i]='_';
97         ++i;
98         Integer.format(res[i..i+8],cmwc_r,cast(char[])"x8");
99         i+=8;
100         foreach (val;cmwc_q){
101             res[i]='_';
102             ++i;
103             Integer.format(res[i..i+8],val,cast(char[])"x8");
104             i+=8;
105         }
106         foreach (val;[cmwc_i,cmwc_c,nBytes,restB]){
107             res[i]='_';
108             ++i;
109             Integer.format(res[i..i+8],val,cast(char[])"x8");
110             i+=8;
111         }
112         assert(i==res.length,"unexpected size");
113         return cast(immutable(char)[])res;
114     }
115     /// reads the current status from a string (that should have been trimmed)
116     /// returns the number of chars read
117     size_t fromString(const(char[]) s){
118         char[16] tmpC;
119         size_t i=0;
120         assert(s[i..i+4]=="CMWC","unexpected kind, expected CMWC");
121         i+=4;
122         assert(s[i..i+16]==Integer.format(tmpC,cmwc_a,"x16"),"unexpected cmwc_a");
123         i+=16;
124         assert(s[i]=='_',"unexpected format, expected _");
125         i++;
126         assert(s[i..i+8]==Integer.format(tmpC,cmwc_r,"x8"),"unexpected cmwc_r");
127         i+=8;
128         foreach (ref val;cmwc_q){
129             assert(s[i]=='_',"no separator _ found");
130             ++i;
131             size_t ate;
132             val=cast(uint)Integer.convert(s[i..i+8],16,&ate);
133             assert(ate==8,"unexpected read size");
134             i+=8;
135         }
136         foreach (val;[&cmwc_i,&cmwc_c,&nBytes,&restB]){
137             assert(s[i]=='_',"no separator _ found");
138             ++i;
139             size_t ate;
140             *val=cast(uint)Integer.convert(s[i..i+8],16,&ate);
141             assert(ate==8,"unexpected read size");
142             i+=8;
143         }
144         return i;
145     }
146 }
147 
148 /// some variations, the first has a period of ~10^39461, the first number (r) is basically the size of the seed
149 /// (and all bit patterns of that size are guarenteed to crop up in the period), the period is 2^(32*r)*a
150 alias CMWC!(4096U,18782UL)     CMWC_4096_1;
151 alias CMWC!(2048U,1030770UL)   CMWC_2048_1;
152 alias CMWC!(2048U,1047570UL)   CMWC_2048_2;
153 alias CMWC!(1024U,5555698UL)   CMWC_1024_1;
154 alias CMWC!(1024U,987769338UL) CMWC_1024_2;
155 alias CMWC!(512U,123462658UL)  CMWC_512_1;
156 alias CMWC!(512U,123484214UL)  CMWC_512_2;
157 alias CMWC!(256U,987662290UL)  CMWC_256_1;
158 alias CMWC!(256U,987665442UL)  CMWC_256_2;
159 alias CMWC!(128U,987688302UL)  CMWC_128_1;
160 alias CMWC!(128U,987689614UL)  CMWC_128_2;
161 alias CMWC!(64U ,987651206UL)  CMWC_64_1;
162 alias CMWC!(64U ,987657110UL)  CMWC_64_2;
163 alias CMWC!(32U ,987655670UL)  CMWC_32_1;
164 alias CMWC!(32U ,987655878UL)  CMWC_32_2;
165 alias CMWC!(16U ,987651178UL)  CMWC_16_1;
166 alias CMWC!(16U ,987651182UL)  CMWC_16_2;
167 alias CMWC!(8U  ,987651386UL)  CMWC_8_1;
168 alias CMWC!(8U  ,987651670UL)  CMWC_8_2;
169 alias CMWC!(4U  ,987654366UL)  CMWC_4_1;
170 alias CMWC!(4U  ,987654978UL)  CMWC_4_2;
171 alias CMWC_1024_2 CMWC_default;