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 18 module ocean.math.random.engines.KissCmwc; 19 import ocean.transition; 20 import Integer = ocean.text.convert.Integer_tango; 21 import ocean.core.Verify; 22 23 /+ CMWC and KISS random number generators combined, for extra security wrt. plain CMWC and 24 + Marisaglia, Journal of Modern Applied Statistical Methods (2003), vol.2,No.1,p 2-13 25 + a simple safe and fast RNG that passes all statistical tests, has a large seed, and is reasonably fast 26 + This is the engine, *never* use it directly, always use it though a RandomG class 27 +/ 28 struct KissCmwc(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 private uint kiss_x = 123456789; 33 private uint kiss_y = 362436000; 34 private uint kiss_z = 521288629; 35 private uint kiss_c = 7654321; 36 uint nBytes = 0; 37 uint restB = 0; 38 39 enum int canCheckpoint=true; 40 enum int canSeed=true; 41 42 void skip(uint n){ 43 for (int i=n;i!=n;--i){ 44 next; 45 } 46 } 47 ubyte nextB(){ 48 if (nBytes>0) { 49 ubyte res=cast(ubyte)(restB & 0xFF); 50 restB >>= 8; 51 --nBytes; 52 return res; 53 } else { 54 restB=next; 55 ubyte res=cast(ubyte)(restB & 0xFF); 56 restB >>= 8; 57 nBytes=3; 58 return res; 59 } 60 } 61 uint next(){ 62 enum uint rMask=cmwc_r-1u; 63 static assert((rMask&cmwc_r)==0,"cmwc_r is supposed to be a power of 2"); // put a more stringent test? 64 enum uint m=0xFFFF_FFFE; 65 cmwc_i=(cmwc_i+1)&rMask; 66 ulong t=cmwc_a*cmwc_q[cmwc_i]+cmwc_c; 67 cmwc_c=cast(uint)(t>>32); 68 uint x=cast(uint)t+cmwc_c; 69 if (x<cmwc_c) { 70 ++x; ++cmwc_c; 71 } 72 enum ulong a = 698769069UL; 73 ulong k_t; 74 kiss_x = 69069*kiss_x+12345; 75 kiss_y ^= (kiss_y<<13); kiss_y ^= (kiss_y>>17); kiss_y ^= (kiss_y<<5); 76 k_t = a*kiss_z+kiss_c; kiss_c = cast(uint)(k_t>>32); 77 kiss_z=cast(uint)k_t; 78 return (cmwc_q[cmwc_i]=m-x)+kiss_x+kiss_y+kiss_z; // xor to avoid overflow? 79 } 80 81 ulong nextL(){ 82 return ((cast(ulong)next)<<32)+cast(ulong)next; 83 } 84 85 void seed(scope uint delegate() rSeed){ 86 kiss_x = rSeed(); 87 for (int i=0;i<100;++i){ 88 kiss_y=rSeed(); 89 if (kiss_y!=0) break; 90 } 91 if (kiss_y==0) kiss_y=362436000; 92 kiss_z=rSeed(); 93 /* Don’t really need to seed c as well (is reset after a next), 94 but doing it allows to completely restore a given internal state */ 95 kiss_c = rSeed() % 698769069; /* Should be less than 698769069 */ 96 97 cmwc_i=cmwc_r-1u; // randomize also this? 98 for (int ii=0;ii<10;++ii){ 99 for (uint i=0;i<cmwc_r;++i){ 100 cmwc_q[i]=rSeed(); 101 } 102 uint nV=rSeed(); 103 uint to=(uint.max/cmwc_a)*cmwc_a; 104 for (int i=0;i<10;++i){ 105 if (nV<to) break; 106 nV=rSeed(); 107 } 108 cmwc_c=cast(uint)(nV%cmwc_a); 109 nBytes = 0; 110 restB=0; 111 if (cmwc_c==0){ 112 for (uint i=0;i<cmwc_r;++i) 113 if (cmwc_q[i]!=0) return; 114 } else if (cmwc_c==cmwc_a-1){ 115 for (uint i=0;i<cmwc_r;++i) 116 if (cmwc_q[i]!=0xFFFF_FFFF) return; 117 } else return; 118 } 119 cmwc_c=1; 120 kiss_x = rSeed(); 121 for (int i=0;i<100;++i){ 122 kiss_y=rSeed(); 123 if (kiss_y!=0) break; 124 } 125 if (kiss_y==0) kiss_y=362436000; 126 kiss_z=rSeed(); 127 /* Don’t really need to seed c as well (is reset after a next), 128 but doing it allows to completely restore a given internal state */ 129 kiss_c = rSeed() % 698769069; /* Should be less than 698769069 */ 130 } 131 /// writes the current status in a string 132 istring toString(){ 133 char[] res=new char[11+16+(cmwc_r+9)*9]; 134 int i=0; 135 res[i..i+11]="CMWC+KISS99"; 136 i+=11; 137 Integer.format(res[i..i+16],cmwc_a,"x16"); 138 i+=16; 139 res[i]='_'; 140 ++i; 141 Integer.format(res[i..i+8],cmwc_r,"x8"); 142 i+=8; 143 foreach (val;cmwc_q){ 144 res[i]='_'; 145 ++i; 146 Integer.format(res[i..i+8],val,"x8"); 147 i+=8; 148 } 149 foreach (val;[cmwc_i,cmwc_c,nBytes,restB,kiss_x,kiss_y,kiss_z,kiss_c]){ 150 res[i]='_'; 151 ++i; 152 Integer.format(res[i..i+8],val,"x8"); 153 i+=8; 154 } 155 verify(i==res.length,"unexpected size"); 156 return assumeUnique(res); 157 } 158 /// reads the current status from a string (that should have been trimmed) 159 /// returns the number of chars read 160 size_t fromString(cstring s){ 161 char[16] tmpC; 162 size_t i=0; 163 verify(s[i..i+11]=="CMWC+KISS99","unexpected kind, expected CMWC+KISS99"); 164 i+=11; 165 verify(s[i..i+16]==Integer.format(tmpC,cmwc_a,"x16"),"unexpected cmwc_a"); 166 i+=16; 167 verify(s[i]=='_',"unexpected format, expected _"); 168 i++; 169 verify(s[i..i+8]==Integer.format(tmpC,cmwc_r,"x8"),"unexpected cmwc_r"); 170 i+=8; 171 foreach (ref val;cmwc_q){ 172 verify(s[i]=='_',"no separator _ found"); 173 ++i; 174 uint ate; 175 val=cast(uint)Integer.convert(s[i..i+8],16,&ate); 176 verify(ate==8,"unexpected read size"); 177 i+=8; 178 } 179 foreach (val;[&cmwc_i,&cmwc_c,&nBytes,&restB,&kiss_x,&kiss_y,&kiss_z,&kiss_c]){ 180 verify(s[i]=='_',"no separator _ found"); 181 ++i; 182 uint ate; 183 *val=cast(uint)Integer.convert(s[i..i+8],16,&ate); 184 verify(ate==8,"unexpected read size"); 185 i+=8; 186 } 187 return i; 188 } 189 } 190 191 /// some variations of the CMWC part, the first has a period of ~10^39461 192 /// the first number (r) is basically the size of the seed and storage (and all bit patterns 193 /// of that size are guarenteed to crop up in the period), the period is (2^32-1)^r*a 194 alias KissCmwc!(4096U,18782UL) KissCmwc_4096_1; 195 alias KissCmwc!(2048U,1030770UL) KissCmwc_2048_1; 196 alias KissCmwc!(2048U,1047570UL) KissCmwc_2048_2; 197 alias KissCmwc!(1024U,5555698UL) KissCmwc_1024_1; 198 alias KissCmwc!(1024U,987769338UL) KissCmwc_1024_2; 199 alias KissCmwc!(512U,123462658UL) KissCmwc_512_1; 200 alias KissCmwc!(512U,123484214UL) KissCmwc_512_2; 201 alias KissCmwc!(256U,987662290UL) KissCmwc_256_1; 202 alias KissCmwc!(256U,987665442UL) KissCmwc_256_2; 203 alias KissCmwc!(128U,987688302UL) KissCmwc_128_1; 204 alias KissCmwc!(128U,987689614UL) KissCmwc_128_2; 205 alias KissCmwc!(64U ,987651206UL) KissCmwc_64_1; 206 alias KissCmwc!(64U ,987657110UL) KissCmwc_64_2; 207 alias KissCmwc!(32U ,987655670UL) KissCmwc_32_1; 208 alias KissCmwc!(32U ,987655878UL) KissCmwc_32_2; 209 alias KissCmwc!(16U ,987651178UL) KissCmwc_16_1; 210 alias KissCmwc!(16U ,987651182UL) KissCmwc_16_2; 211 alias KissCmwc!(8U ,987651386UL) KissCmwc_8_1; 212 alias KissCmwc!(8U ,987651670UL) KissCmwc_8_2; 213 alias KissCmwc!(4U ,987654366UL) KissCmwc_4_1; 214 alias KissCmwc!(4U ,987654978UL) KissCmwc_4_2; 215 alias KissCmwc_1024_2 KissCmwc_default;