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