1 /** 2 *xoshiro generators 3 *Authors: lempiji 4 */ 5 module xoshiro.xoshiro; 6 7 import xoshiro.util; 8 9 import std.random; 10 11 /// 12 enum XoshiroOpMode 13 { 14 Plus, 15 PlusPlus, 16 StarStar, 17 } 18 19 /// 20 struct XoshiroEngine(UIntType, XoshiroOpMode mode) 21 { 22 static assert(is(UIntType == uint) || is(UIntType == ulong)); 23 24 enum isUniformRandom = true; 25 enum min = UIntType(0); 26 enum max = UIntType.max; 27 28 private: 29 UIntType[4] state; 30 31 enum shiftSize = UIntType.sizeof == 8 ? 17 : 9; 32 enum rotSize = UIntType.sizeof == 8 ? 45 : 11; 33 enum UIntBits = UIntType.sizeof * 8; 34 35 public: 36 /// 37 this()() 38 { 39 this.seed = 0; 40 } 41 42 /// 43 this()(UIntType seed) 44 { 45 this.seed = seed; 46 } 47 48 /// 49 enum empty = false; 50 51 /// 52 UIntType front() const pure nothrow @safe @nogc @property 53 { 54 static if (mode == XoshiroOpMode.Plus) 55 return state[0] + state[3]; 56 else static if (mode == XoshiroOpMode.PlusPlus) 57 return rotl(state[0] + state[3], 23) + state[0]; 58 else static if (mode == XoshiroOpMode.StarStar) 59 return rotl(state[1] * 5, 7) * 9; 60 else 61 static assert(false); 62 } 63 64 /// 65 void popFront() @safe pure nothrow @nogc 66 { 67 const UIntType t = state[1] << shiftSize; 68 69 state[2] ^= state[0]; 70 state[3] ^= state[1]; 71 state[1] ^= state[2]; 72 state[0] ^= state[3]; 73 74 state[2] ^= t; 75 76 state[3] = rotl(state[3], rotSize); 77 } 78 79 /// 80 typeof(this) save() const @safe pure nothrow @nogc @property 81 { 82 return this; 83 } 84 85 /// 86 void seed(UIntType seed) @safe pure nothrow @nogc 87 { 88 ulong temp = seed; 89 state[0] = cast(UIntType) splitMix(temp); 90 state[1] = cast(UIntType) splitMix(temp); 91 state[2] = cast(UIntType) splitMix(temp); 92 state[3] = cast(UIntType) splitMix(temp); 93 } 94 95 ///It is equivalent to 2^N calls to popFront() 96 /// 97 /// N = 128 on Xoshiro256 98 /// N = 64 on Xoshiro128 99 void jump() 100 { 101 static if (UIntType.sizeof == 8) 102 { 103 // 256 104 enum ulong[] JUMP = [ 105 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 106 0x39abdc4529b1661c 107 ]; 108 } 109 else 110 { 111 // 128 112 enum uint[] JUMP = [ 113 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b 114 ]; 115 } 116 117 UIntType s0 = 0; 118 UIntType s1 = 0; 119 UIntType s2 = 0; 120 UIntType s3 = 0; 121 foreach (jump; JUMP) 122 { 123 foreach (b; 0 .. UIntBits) 124 { 125 if (jump & (UIntType(1) << b)) 126 { 127 s0 ^= state[0]; 128 s1 ^= state[1]; 129 s2 ^= state[2]; 130 s3 ^= state[3]; 131 } 132 popFront(); 133 } 134 } 135 136 state[0] = s0; 137 state[1] = s1; 138 state[2] = s2; 139 state[3] = s3; 140 } 141 142 ///It is equivalent to 2^N calls to popFront() 143 /// 144 /// N = 192 on Xoshiro256 145 /// N = 96 on Xoshiro128 146 void longJump() 147 { 148 static if (UIntType.sizeof == 8) 149 { 150 // 256 151 enum ulong[] LONG_JUMP = [ 152 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 153 0x39109bb02acbe635 154 ]; 155 } 156 else 157 { 158 // 128 159 enum uint[] LONG_JUMP = [ 160 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 161 ]; 162 } 163 UIntType s0 = 0; 164 UIntType s1 = 0; 165 UIntType s2 = 0; 166 UIntType s3 = 0; 167 foreach (jump; LONG_JUMP) 168 { 169 foreach (b; 0 .. UIntBits) 170 { 171 if (jump & (UIntType(1) << b)) 172 { 173 s0 ^= state[0]; 174 s1 ^= state[1]; 175 s2 ^= state[2]; 176 s3 ^= state[3]; 177 } 178 popFront(); 179 } 180 } 181 state[0] = s0; 182 state[1] = s1; 183 state[2] = s2; 184 state[3] = s3; 185 } 186 } 187 188 /** 189 * Xoshiro256+ 190 * Period: 2 ^ 256 - 1 191 * Footprint: 32 bytes 192 */ 193 alias Xoshiro256Plus = XoshiroEngine!(ulong, XoshiroOpMode.Plus); 194 195 /// ditto 196 @("Overview Xoshiro256+") 197 unittest 198 { 199 import std.random : uniform01; 200 201 auto rndGen = Xoshiro256Plus(unpredictableSeed!ulong); 202 auto x = uniform01(rndGen); 203 assert(0 <= x && x <= 1); 204 } 205 206 /** 207 * Xoshiro256++ 208 * Period: 2 ^ 256 - 1 209 * Footprint: 32 bytes 210 */ 211 alias Xoshiro256PlusPlus = XoshiroEngine!(ulong, XoshiroOpMode.PlusPlus); 212 213 /// ditto 214 @("Overview Xoshiro256++") 215 unittest 216 { 217 import std.random : uniform01; 218 219 auto rndGen = Xoshiro256PlusPlus(unpredictableSeed!ulong); 220 auto x = uniform01(rndGen); 221 assert(0 <= x && x <= 1); 222 } 223 224 /** 225 * Xoshiro256** 226 * Period: 2 ^ 256 - 1 227 * Footprint: 32 bytes 228 */ 229 alias Xoshiro256StarStar = XoshiroEngine!(ulong, XoshiroOpMode.StarStar); 230 231 /// ditto 232 @("Overview Xoshiro256**") 233 unittest 234 { 235 import std.random : uniform01; 236 237 auto rndGen = Xoshiro256StarStar(unpredictableSeed!ulong); 238 auto x = uniform01(rndGen); 239 assert(0 <= x && x <= 1); 240 } 241 242 /** 243 * Xoshiro128+ 244 * Period: 2 ^ 128 - 1 245 * Footprint: 16 bytes 246 */ 247 alias Xoshiro128Plus = XoshiroEngine!(uint, XoshiroOpMode.Plus); 248 249 /// ditto 250 @("Overview Xoshiro128+") 251 unittest 252 { 253 import std.random : uniform01; 254 255 auto rndGen = Xoshiro128Plus(unpredictableSeed); 256 auto x = uniform01(rndGen); 257 assert(0 <= x && x <= 1); 258 } 259 260 /** 261 * Xoshiro128++ 262 * Period: 2 ^ 128 - 1 263 * Footprint: 16 bytes 264 */ 265 alias Xoshiro128PlusPlus = XoshiroEngine!(uint, XoshiroOpMode.PlusPlus); 266 267 /// ditto 268 @("Overview Xoshiro128++") 269 unittest 270 { 271 import std.random : uniform01; 272 273 auto rndGen = Xoshiro128PlusPlus(unpredictableSeed); 274 auto x = uniform01(rndGen); 275 assert(0 <= x && x <= 1); 276 } 277 278 /** 279 * Xoshiro128** 280 * Period: 2 ^ 128 - 1 281 * Footprint: 16 bytes 282 */ 283 alias Xoshiro128StarStar = XoshiroEngine!(uint, XoshiroOpMode.StarStar); 284 285 /// ditto 286 @("Overview Xoshiro128**") 287 unittest 288 { 289 import std.random : uniform01; 290 291 auto rndGen = Xoshiro128StarStar(unpredictableSeed); 292 auto x = uniform01(rndGen); 293 assert(0 <= x && x <= 1); 294 } 295 296 @("isInfinityForwardRange && isUniformRNG && isSeedable") 297 unittest 298 { 299 import std.range; 300 import std.meta : AliasSeq; 301 302 alias Ts = AliasSeq!( 303 Xoshiro128Plus, Xoshiro128PlusPlus, Xoshiro128StarStar, 304 Xoshiro256Plus, Xoshiro256PlusPlus, Xoshiro256StarStar); 305 306 static foreach (T; Ts) 307 { 308 static assert(isInfinite!T); 309 static assert(isForwardRange!T); 310 static assert(isUniformRNG!T); 311 static assert(isSeedable!(T, ElementType!T)); 312 } 313 }