001package squidpony.squidmath; 002 003import squidpony.StringKit; 004 005import java.io.Serializable; 006import java.util.Arrays; 007 008/** 009 * An RNG that has a drastically longer period than the other generators in SquidLib, other than {@link IsaacRNG}, 010 * without sacrificing speed or GWT support. If you don't already know what the period of an RNG is, this probably 011 * isn't needed for your purposes, or many purposes in games at all. It is primarily meant for applications that need to 012 * generate massive amounts of random numbers, more than pow(2, 64) (18,446,744,073,709,551,616), without repeating 013 * the sequence of generated numbers. An RNG's period refers to the number of numbers it generates given a single 014 * seed before the sequence repeats from the beginning. The period of this class is pow(2, 1024) minus 1 015 * (179,769,313,486,231,590,772,930,519,078,902,473,361,797,697,894,230,657,273,430,081,157,732,675,805,500,963,132,708, 016 * 477,322,407,536,021,120,113,879,871,393,357,658,789,768,814,416,622,492,847,430,639,474,124,377,767,893,424,865,485, 017 * 276,302,219,601,246,094,119,453,082,952,085,005,768,838,150,682,342,462,881,473,913,110,540,827,237,163,350,510,684, 018 * 586,298,239,947,245,938,479,716,304,835,356,329,624,224,137,215). While that number is preposterously large, there's 019 * always some application that seems to need more; if you really need more than that, look into CMWC generators, which 020 * can have even larger state and also even larger periods. There isn't one of those in SquidLib currently, though there 021 * is a possibility of one being added in the future. There is a 64-bit MersenneTwister, which has an even larger period 022 * than this one, but it might not have optimal quality for some applications (notably, the game Dungeon Crawl Stone 023 * Soup used Mersenne Twister and found that some players in a competition could predict impending random events, 024 * despite the generator seeming bulletproof). 025 * <br> 026 * This class may be particularly useful in conjunction with the shuffle method of RNG; the period of an RNG determines 027 * how many possible "shuffles", a.k.a orderings or permutations, can be produced over all calls to a permuting method 028 * like shuffle. A LightRNG-backed RNG with a period of pow(2, 64) will only be able to produce all possible "shuffles" 029 * for lists or arrays of 20 items or less. If a LongPeriodRNG is given to the constructor of an RNG and a large enough 030 * state has been given to the LongPeriodRNG (the String or long[] constructors can allow this), then lists or arrays of 031 * up to 170 elements can have all possible orderings produced by shuffle(), though it will take near-impossibly-many 032 * calls. This class has 128 bytes of state plus more in overhead (compare to the 16-byte-with-overhead LightRNG), but 033 * due to its massive period and createMany() static method, you can create a large number of subsequences with rather 034 * long periods themselves from a single seed. This uses the xorshift-1024*phi algorithm, and has competitive speed. 035 * <br> 036 * This generator was updated to the "phi" variant of XorShift1024* instead of the "M_8" variant when the phi variant 037 * became recommended over the version this originally used. The multiplier, and thus the sequence of numbers this 038 * generates for a given seed, changed on October 19, 2017. 039 * <br> 040 * Created by Tommy Ettinger on 3/21/2016. 041 * Ported from CC0-licensed C code by Sebastiano Vigna, at http://xorshift.di.unimi.it/xorshift1024star.c 042 * @author Tommy Ettinger 043 */ 044public final class LongPeriodRNG implements RandomnessSource, Serializable { 045 046 public final long[] state = new long[16]; 047 public int choice; 048 private static final long serialVersionUID = 173524490381383244L; 049 private static final long[] jumpTable = {0x84242f96eca9c41dL, 050 0xa3c65b8776f96855L, 0x5b34a39f070b5837L, 0x4489affce4f31a1eL, 051 0x2ffeeb0a48316f40L, 0xdc2d9891fe68c022L, 0x3659132bb12fea70L, 052 0xaac17d8efa43cab8L, 0xc4cb815590989b13L, 0x5ee975283d71c93bL, 053 0x691548c86c1bd540L, 0x7910c41d10a1e6a5L, 0x0b5fc64563b3e2a8L, 054 0x047f7684e9fc949dL, 0xb99181f2d8f685caL, 0x284600e3f30e38c3L 055 }; 056 057 /** 058 * Builds a LongPeriodRNG and initializes this class' 1024 bits of state with a random seed passed into SplitMix64, 059 * the algorithm also used by LightRNG. A different algorithm is used in non-constructor code to generate random 060 * numbers, which is a recommended technique to generate seeds. 061 */ 062 public LongPeriodRNG() { 063 reseed(); 064 } 065 066 /** 067 * Builds a LongPeriodRNG and initializes this class' 1024 bits of state with many calls to a SplitMix64-based RNG 068 * using a variant on seed produced by running it through PCG-Random's output step (PermutedRNG here). 069 * @param seed a 64-bit seed; can be any value. 070 */ 071 public LongPeriodRNG(long seed) { 072 reseed(seed); 073 } 074 075 public void reseed() { 076 077 long ts = LightRNG.determine((long) ((Math.random() * 2.0 - 1.0) * 0x8000000000000L) 078 ^ (long) ((Math.random() * 2.0 - 1.0) * 0x8000000000000000L)); 079 choice = (int) (ts & 15); 080 state[0] = ~(ts >>> 1); 081 for (int i = 1; i < 16; i++) { 082 //Chosen by trial and error to unevenly reseed 4 times, where i is 2, 5, 10, or 13 083 if ((6 & (i * 1281783497376652987L)) == 6) 084 ts ^= (long) ((Math.random() * 2.0 - 1.0) * 0x8000000000000L) 085 ^ (long) ((Math.random() * 2.0 - 1.0) * 0x8000000000000000L); 086 state[i - 1] ^= (state[i] = LightRNG.determine(++ts)); 087 } 088 if (state[0] == 0L) state[0] = -17; 089 090 } 091 092 /** 093 * Reinitializes this class' 1024 bits of state with the given seed passed into SplitMix64, the algorithm also used by 094 * LightRNG. A different algorithm is used in actual number generating code, which is a recommended technique to 095 * generate seeds. 096 * 097 * @param seed a 64-bit seed; can be any value. 098 */ 099 public void reseed(long seed) { 100 init(seed); 101 choice = (int) (seed & 15); 102 } 103 104 /** 105 * Builds a LongPeriodRNG and initializes this class' 1024 bits of state with the given seed, using a different 106 * strategy depending on the seed. If seed is null, this uses the same state as any other null seed. If seed is a 107 * String with length 15 or less, this generates a 64-bit hash of the seed and uses it in the same way the constructor 108 * that takes a long creates 1024 bits of state from a 64-bit seed. If seed is a String with length 16 or more, this 109 * splits the string up and generates 16 hashes from progressively smaller substrings of seed. The highest quality 110 * states will result from passing this a very long String (a StringBuilder would also be a good choice). 111 * 112 * @param seed a String (or other CharSequence) seed; can be any value, but produces the best results if it at least 16 characters long 113 */ 114 public LongPeriodRNG(CharSequence seed) { 115 reseed(seed); 116 } 117 118 /** 119 * Reinitializes this class' 1024 bits of state with the given seed, using a different strategy depending on the seed. 120 * If seed is null, this uses the same state as any other null seed. If seed is a String with length 15 or less, this 121 * generates a 64-bit hash of the seed and uses it in the same way the constructor that takes a long creates 1024 bits 122 * of state from a 64-bit seed. If seed is a String with length 16 or more, this splits the string up and generates 16 123 * hashes from progressively smaller substrings of seed. The highest quality states will result from passing this a 124 * very long String (a StringBuilder would also be a good choice). 125 * 126 * @param seed a String (or other CharSequence) seed; can be any value, but produces the best results if it at least 16 characters long 127 */ 128 public void reseed(CharSequence seed) { 129 int len; 130 if (seed == null || (len = seed.length()) == 0) { 131 init(0x632BE59BD9B4E019L); 132 choice = 0; 133 } else { 134 if (len < 16) { 135 long h = CrossHash.hash64(seed); 136 init(h); 137 choice = (int) (h & 15); 138 } else { 139 state[0] = validate(CrossHash.hash64(seed)); 140 for (int i = 0; i < 16; i++) { 141 state[i] = validate(CrossHash.hash64(seed, i * len >> 4, len)); 142 } 143 choice = (int) (state[0] & 15); 144 } 145 } 146 } 147 148 /** 149 * Builds a LongPeriodRNG and initializes this class' 1024 bits of state with the given seed as a long array, which 150 * may or may not have 16 elements (though it is less wasteful to run this with 16 longs since that is exactly 1024 151 * bits). If seed is null, this produces the same state as the String constructor does when given a null seed. If seed 152 * has fewer than 16 elements, this repeats earlier elements once it runs out of unused longs. If seed has 16 or more 153 * elements, this exclusive-ors elements after the sixteenth with longs it has already placed into the state, causing 154 * all elements of the seed to have an effect on the state, and making the 16-element case copy all longs exactly. 155 * 156 * @param seed a long array seed; can have any number of elements, though 16 is ideal 157 */ 158 public LongPeriodRNG(long[] seed) { 159 reseed(seed); 160 } 161 162 /** 163 * Reinitializes this class' 1024 bits of state with the given seed as a long array, which may or may not have 16 164 * elements (though it is less wasteful to run this with 16 longs since that is exactly 1024 bits). If seed is null, 165 * this produces the same state as the String constructor does when given a null seed. If seed has fewer than 16 166 * elements, this repeats earlier elements once it runs out of unused longs. If seed has 16 or more elements, this 167 * exclusive-ors elements after the sixteenth with longs it has already placed into the state, causing all elements of 168 * the seed to have an effect on the state, and making the 16-element case copy all longs exactly. 169 * 170 * @param seed a long array seed; can have any number of elements, though 16 is ideal 171 */ 172 public void reseed(long[] seed) { 173 int len; 174 if (seed == null || (len = seed.length) == 0) { 175 init(0x632BE59BD9B4E019L); 176 choice = 0; 177 } else if (len < 16) { 178 for (int i = 0, s = 0; i < 16; i++, s++) { 179 if(s == len) s = 0; 180 state[i] ^= seed[s]; 181 if (state[i] == 0) state[i] = 1; 182 } 183 choice = (int) (state[0] & 15); 184 } else { 185 for (int i = 0, s = 0; s < len; s++, i = (i + 1) & 15) { 186 state[i] ^= seed[s]; 187 if (state[i] == 0) state[i] = 1; 188 } 189 choice = (int) (state[0] & 15); 190 } 191 } 192 193 private static long validate(long seed) { 194 if (seed == 0) return 1; 195 else return seed; 196 } 197 198 private void init(long seed) { 199 long z; 200 seed ^= seed >>> (5 + (seed >>> 59)); 201 seed = ((seed *= 0xAEF17502108EF2D9L) >>> 43) ^ seed; 202 for (int i = 0; i < 16; i++) { 203 z = (seed += 0x9E3779B97F4A7C15L); 204 z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; 205 z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; 206 state[i] = z ^ (z >>> 31); 207 if (state[i] == 0) state[i] = 1; 208 } 209 } 210 211 public LongPeriodRNG(LongPeriodRNG other) { 212 choice = other.choice; 213 System.arraycopy(other.state, 0, state, 0, 16); 214 } 215 216 @Override 217 public int next(int bits) { 218 final long s0 = state[choice]; 219 long s1 = state[choice = (choice + 1) & 15]; 220 s1 ^= s1 << 31; 221 return (int) ((state[choice] = s1 ^ s0 ^ (s1 >>> 11) ^ (s0 >>> 30)) * 0x9E3779B97F4A7C13L >>> (64 - bits)); 222 } 223 224 /** 225 * Can return any long, positive or negative, of any size permissible in a 64-bit signed integer. 226 * <br> 227 * Written by Sebastiano Vigna, from http://xorshift.di.unimi.it/xorshift1024star.c 228 * 229 * @return any long, all 64 bits are random 230 */ 231 // Previously used multiplier 1181783497276652981L ; this is the "phi" variant instead of "M_8" 232 // See http://xoroshiro.di.unimi.it/xorshift1024star.c for details 233 @Override 234 public long nextLong() { 235 final long s0 = state[choice]; 236 long s1 = state[choice = (choice + 1) & 15]; 237 s1 ^= s1 << 31; 238 return (state[choice] = s1 ^ s0 ^ (s1 >>> 11) ^ (s0 >>> 30)) * 0x9E3779B97F4A7C13L; 239 } 240 241 /** 242 * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the 243 * copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to 244 * copy the state so it isn't shared, usually, and produce a new value with the same exact state. 245 * 246 * @return a copy of this RandomnessSource 247 */ 248 @Override 249 public LongPeriodRNG copy() { 250 LongPeriodRNG next = new LongPeriodRNG(); 251 System.arraycopy(state, 0, next.state, 0, 16); 252 next.choice = choice; 253 return next; 254 255 } 256 257 /** 258 * This is the jump function for the generator. It is equivalent to 2^512 calls to nextLong(); it can be used to 259 * generate 2^512 non-overlapping subsequences for parallel computations. Alters the state of this object. 260 * <br> 261 * Written by Sebastiano Vigna, from http://xorshift.di.unimi.it/xorshift1024star.c , don't ask how it works. 262 */ 263 public void jump() { 264 265 long[] t = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 266 for (int i = 0; i < 16; i++) 267 for (int b = 0; b < 64; b++) { 268 if ((jumpTable[i] & 1L << b) != 0) { 269 for (int j = 0; j < 16; j++) 270 t[j] ^= state[(j + choice) & 15]; 271 } 272 nextLong(); 273 } 274 275 for (int j = 0; j < 16; j++) 276 state[(j + choice) & 15] = t[j]; 277 } 278 279 /** 280 * Creates many LongPeriodRNG objects in an array, where each will generate a sequence of pow(2, 512) numbers that 281 * will not overlap with other sequences in the array. The number of items in the array is specified by count. 282 * 283 * @param count the number of LongPeriodRNG objects to generate in the array. 284 * @return an array of LongPeriodRNG where none of the RNGs will generate overlapping sequences. 285 */ 286 public static LongPeriodRNG[] createMany(int count) { 287 if (count < 1) count = 1; 288 LongPeriodRNG origin = new LongPeriodRNG(); 289 LongPeriodRNG[] values = new LongPeriodRNG[count]; 290 for (int i = count - 1; i > 0; i--) { 291 values[i] = new LongPeriodRNG(origin); 292 origin.jump(); 293 } 294 values[0] = origin; 295 296 return values; 297 } 298 299 /** 300 * Creates many LongPeriodRNG objects in an array, where each will generate a sequence of pow(2, 512) numbers that 301 * will not overlap with other sequences in the array. The number of items in the array is specified by count. A 302 * seed can be given that will affect all items in the array, but with each item using a different section of the 303 * massive period this class supports. Essentially, each LongPeriodRNG in the array will generate a different random 304 * sequence relative to any other element of the array, but the sequences are reproducible if the same seed is given 305 * to this method a different time (useful for testing). 306 * 307 * @param count the number of LongPeriodRNG objects to generate in the array. 308 * @param seed the RNG seed that will determine the different sequences the returned LongPeriodRNG objects produce 309 * @return an array of LongPeriodRNG where none of the RNGs will generate overlapping sequences. 310 */ 311 public static LongPeriodRNG[] createMany(int count, long seed) { 312 if (count < 1) count = 1; 313 LongPeriodRNG origin = new LongPeriodRNG(seed); 314 LongPeriodRNG[] values = new LongPeriodRNG[count]; 315 for (int i = count - 1; i > 0; i--) { 316 values[i] = new LongPeriodRNG(origin); 317 origin.jump(); 318 } 319 values[0] = origin; 320 return values; 321 } 322 323 /** 324 * Creates many LongPeriodRNG objects in an array, where each will generate a sequence of pow(2, 512) numbers that 325 * will not overlap with other sequences in the array. The number of items in the array is specified by count. A 326 * seed can be given that will affect all items in the array, but with each item using a different section of the 327 * massive period this class supports. Essentially, each LongPeriodRNG in the array will generate a different random 328 * sequence relative to any other element of the array, but the sequences are reproducible if the same seed is given 329 * to this method a different time (useful for testing). 330 * 331 * @param count the number of LongPeriodRNG objects to generate in the array. 332 * @param seed the RNG seed that will determine the different sequences the returned LongPeriodRNG objects produce 333 * @return an array of LongPeriodRNG where none of the RNGs will generate overlapping sequences. 334 */ 335 public static LongPeriodRNG[] createMany(int count, String seed) { 336 if (count < 1) count = 1; 337 LongPeriodRNG origin = new LongPeriodRNG(seed); 338 LongPeriodRNG[] values = new LongPeriodRNG[count]; 339 for (int i = count - 1; i > 0; i--) { 340 values[i] = new LongPeriodRNG(origin); 341 origin.jump(); 342 } 343 values[0] = origin; 344 return values; 345 } 346 347 /** 348 * Creates many LongPeriodRNG objects in an array, where each will generate a sequence of pow(2, 512) numbers that 349 * will not overlap with other sequences in the array. The number of items in the array is specified by count. A 350 * seed can be given that will affect all items in the array, but with each item using a different section of the 351 * massive period this class supports. Essentially, each LongPeriodRNG in the array will generate a different random 352 * sequence relative to any other element of the array, but the sequences are reproducible if the same seed is given 353 * to this method a different time (useful for testing). 354 * 355 * @param count the number of LongPeriodRNG objects to generate in the array. 356 * @param seed the RNG seed that will determine the different sequences the returned LongPeriodRNG objects produce 357 * @return an array of LongPeriodRNG where none of the RNGs will generate overlapping sequences. 358 */ 359 public static LongPeriodRNG[] createMany(int count, long[] seed) { 360 if (count < 1) count = 1; 361 LongPeriodRNG origin = new LongPeriodRNG(seed); 362 LongPeriodRNG[] values = new LongPeriodRNG[count]; 363 for (int i = count - 1; i > 0; i--) { 364 values[i] = new LongPeriodRNG(origin); 365 origin.jump(); 366 } 367 values[0] = origin; 368 return values; 369 } 370 371 372 @Override 373 public String toString() { 374 return "LongPeriodRNG with state hash 0x" + StringKit.hexHash(state) + "L, choice 0x" + StringKit.hex(choice); 375 } 376 377 @Override 378 public boolean equals(Object o) { 379 if (this == o) return true; 380 if (o == null || getClass() != o.getClass()) return false; 381 382 LongPeriodRNG that = (LongPeriodRNG) o; 383 384 if (choice != that.choice) return false; 385 return Arrays.equals(state, that.state); 386 387 } 388 389 @Override 390 public int hashCode() { 391 return CrossHash.Mist.predefined[choice].hash(state); 392 } 393}