001package squidpony.squidmath; 002 003import squidpony.StringKit; 004 005import java.io.Serializable; 006 007/** 008 * An IStatefulRNG implementation that is meant to provide random numbers very quickly when targeting GWT but also to 009 * produce the same numbers when used on desktop, Android, or other platforms, and that can have its state read as a 010 * StatefulRandomness; it is thus like {@link GWTRNG} but should perform better on recent desktop JVMs. This uses a 011 * related algorithm to {@link OrbitRNG}, modified to use 32-bit math and more stringently randomize output. It has two 012 * 32-bit ints for state and a period of 0x10000000000000000 (2 to the 64), while passing 32TB of PractRand tests 013 * without any failures or anomalies (so its quality is very good). It is extremely fast when run on Java 13, at least 014 * using OpenJ9 as the compiler; it can produce a billion ints a second on moderately-recent laptop hardware. A nice 015 * quality of the implementation is that it allows any int for both of its states, so you don't need to check and avoid 016 * setting both states to 0 (which GWTRNG has to do in its internals). 017 * <br> 018 * Internally, this uses two Weyl sequences (counters with different large increments), and rarely but at specific 019 * intervals (when stateA is 0) introduces a lag into one sequence (making stateB keep its value instead of incrementing 020 * for one generated number). A multiplier is taken from the upper bits of stateB and multiplied with a xorshifted 021 * stateA, then part of a unary hash in the style of SplitMix64 is used to improve quality. A particularly devious piece 022 * of bitwise hackery allows this to avoid branching as it decides whether to add the lag or not, and is responsible for 023 * this implementation's high speed. 024 * <br> 025 * The name comes from spider silk, used in a web, and how this is optimized for Google Web Toolkit. 026 * <br> 027 * This was changed on February 29, 2020 to reduce correlation between very similar generators with the same stateA but 028 * different stateB; it still passes 32TB of PractRand without anomalies, but may be slightly slower. The reasoning here 029 * is that users may want to initialize their RNGs in varied ways, and none of those ways should be unexpectedly flawed. 030 * A similar change was applied to TangleRNG, which is much like a 64-bit simplified version of SilkRNG, 4 days later. 031 * <br> 032 * Written in 2019 by Tommy Ettinger. 033 * @author Tommy Ettinger 034 */ 035public final class SilkRNG extends AbstractRNG implements IStatefulRNG, Serializable { 036 private static final long serialVersionUID = 5L; 037 038 public int stateA, stateB; 039 040 /** 041 * Creates a new generator seeded using two calls to Math.random(). 042 */ 043 public SilkRNG() 044 { 045 setState((int)((Math.random() - 0.5) * 0x1p32), (int)((Math.random() - 0.5) * 0x1p32)); 046 } 047 /** 048 * Constructs this SilkRNG by dispersing the bits of seed using {@link #setSeed(int)} across the two parts of state 049 * this has. 050 * @param seed an int that won't be used exactly, but will affect both components of state 051 */ 052 public SilkRNG(final int seed) { 053 setSeed(seed); 054 } 055 /** 056 * Constructs this SilkRNG by splitting the given seed across the two parts of state this has with 057 * {@link #setState(long)}. 058 * @param seed a long that will be split across both components of state 059 */ 060 public SilkRNG(final long seed) { 061 setState(seed); 062 } 063 /** 064 * Constructs this SilkRNG by calling {@link #setState(int, int)} on stateA and stateB as given; see that method 065 * for the specific details (stateA and stateB are kept as-is). 066 * @param stateA the number to use as the first part of the state 067 * @param stateB the number to use as the second part of the state 068 */ 069 public SilkRNG(final int stateA, final int stateB) { 070 setState(stateA, stateB); 071 } 072 073 /** 074 * Hashes {@code seed} using both {@link CrossHash#hash(CharSequence)} and {@link String#hashCode()} and uses those 075 * two results as the two states with {@link #setState(int, int)}. If seed is null, this won't call 076 * String.hashCode() on it and will instead use 0 as that state. 077 * @param seed any String; may be null 078 */ 079 public SilkRNG(final String seed) { 080 setState(CrossHash.hash(seed), seed == null ? 0 : seed.hashCode()); 081 } 082 083 /** 084 * Get up to 32 bits (inclusive) of random output; the int this produces 085 * will not require more than {@code bits} bits to represent. 086 * 087 * @param bits an int between 1 and 32, both inclusive 088 * @return a random number that fits in the specified number of bits 089 */ 090 @Override 091 public final int next(int bits) { 092 final int s = (stateA += 0xC1C64E6D); 093 final int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 094 int x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 095 x = (x ^ x >>> 16) * 0xAC451; 096 return (x ^ x >>> 15) >>> (32 - bits); 097 } 098 099 /** 100 * Get a random integer between Integer.MIN_VALUE to Integer.MAX_VALUE (both inclusive). 101 * 102 * @return a 32-bit random int. 103 */ 104 @Override 105 public final int nextInt() { 106 final int s = (stateA += 0xC1C64E6D); // Weyl sequence, period is 2 to the 32 107 final int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); // updates stateB only when s != 0 108 int x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); // mix s and t; (xorshifted s) gets multiplied by a negative odd number 109 x = (x ^ x >>> 16) * 0xAC451; // extra strengthening step; multiplier needs to be small for GWT 110 return (x ^ x >>> 15); // closing xorshift to bring the randomizing effect from multiplication to lower bits 111 } 112 /** 113 * Returns a random non-negative integer below the given bound, or 0 if the bound is 0 or 114 * negative. 115 * 116 * @param bound the upper bound (exclusive) 117 * @return the found number 118 */ 119 @Override 120 public final int nextInt(final int bound) { 121 final int s = (stateA += 0xC1C64E6D); 122 final int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 123 int x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 124 x = (x ^ x >>> 16) * 0xAC451; 125 return (int) ((bound * ((x ^ x >>> 15) & 0xFFFFFFFFL)) >>> 32) & ~(bound >> 31); 126 } 127 128 /** 129 * Get a random long between Long.MIN_VALUE to Long.MAX_VALUE (both inclusive). 130 * 131 * @return a 64-bit random long. 132 */ 133 @Override 134 public final long nextLong() { 135 int s = (stateA + 0xC1C64E6D); 136 int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 137 int x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 138 x = (x ^ x >>> 16) * 0xAC451; 139 final long high = (x ^ x >>> 15); 140 s = (stateA += 0x838C9CDA); 141 t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 142 x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 143 x = (x ^ x >>> 16) * 0xAC451; 144 return (high << 32) | ((x ^ x >>> 15) & 0xFFFFFFFFL); 145 } 146 147 /** 148 * Get a random bit of state, interpreted as true or false with approximately equal likelihood. 149 * This implementation uses a sign check as an optimization. 150 * 151 * @return a random boolean. 152 */ 153 @Override 154 public final boolean nextBoolean() { 155 final int s = (stateA += 0xC1C64E6D); 156 final int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 157 return (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE) < 0; 158 } 159 160 /** 161 * Gets a random double between 0.0 inclusive and 1.0 exclusive. 162 * This returns a maximum of 0.9999999999999999 because that is the largest double value that is less than 1.0 . 163 * 164 * @return a double between 0.0 (inclusive) and 0.9999999999999999 (inclusive) 165 */ 166 @Override 167 public final double nextDouble() { 168 int s = (stateA + 0xC1C64E6D); 169 int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 170 int x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 171 x = (x ^ x >>> 16) * 0xAC451; 172 final long high = (x ^ x >>> 15); 173 s = (stateA += 0x838C9CDA); 174 t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 175 x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 176 x = (x ^ x >>> 16) * 0xAC451; 177 return (((high << 32) | ((x ^ x >>> 15) & 0xFFFFFFFFL)) 178 & 0x1FFFFFFFFFFFFFL) * 0x1p-53; 179 } 180 181 /** 182 * Gets a random float between 0.0f inclusive and 1.0f exclusive. 183 * This returns a maximum of 0.99999994 because that is the largest float value that is less than 1.0f . 184 * 185 * @return a float between 0f (inclusive) and 0.99999994f (inclusive) 186 */ 187 @Override 188 public final float nextFloat() { 189 final int s = (stateA += 0xC1C64E6D); 190 final int t = (stateB += (s | -s) >> 31 & 0x9E3779BB); 191 int x = (s ^ s >>> 17) * ~((t ^ t >>> 12) & 0x1FFFFE); 192 x = (x ^ x >>> 16) * 0xAC451; 193 return ((x ^ x >>> 15) & 0xffffff) * 0x1p-24f; 194 } 195 196 /** 197 * Creates a copy of this SilkRNG; it will generate the same random numbers, given the same calls in order, as this 198 * SilkRNG at the point copy() is called. The copy will not share references with this SilkRNG. 199 * 200 * @return a copy of this SilkRNG 201 */ 202 @Override 203 public SilkRNG copy() { 204 return new SilkRNG(stateA, stateB); 205 } 206 207 /** 208 * Gets a view of this IRNG in a way that implements {@link Serializable}, which is simply this IRNG. 209 * @return a {@link Serializable} view of this IRNG or a similar one; always {@code this} 210 */ 211 @Override 212 public Serializable toSerializable() { 213 return this; 214 } 215 /** 216 * Sets the state of this generator using one int, running it through Zog32RNG's algorithm two times to get 217 * two ints. If the states would both be 0, state A is assigned 1 instead. 218 * @param seed the int to use to produce this generator's state 219 */ 220 public void setSeed(final int seed) { 221 int z = seed + 0xC74EAD55, a = seed ^ z; 222 a ^= a >>> 14; 223 z = (z ^ z >>> 10) * 0xA5CB3; 224 a ^= a >>> 15; 225 stateA = (z ^ z >>> 20) + (a ^= a << 13); 226 z = seed + 0x8E9D5AAA; 227 a ^= a >>> 14; 228 z = (z ^ z >>> 10) * 0xA5CB3; 229 a ^= a >>> 15; 230 stateB = (z ^ z >>> 20) + (a ^ a << 13); 231 } 232 233 public int getStateA() 234 { 235 return stateA; 236 } 237 /** 238 * Sets the first part of the state to the given int. 239 * @param stateA any int 240 */ 241 242 public void setStateA(int stateA) 243 { 244 this.stateA = stateA; 245 } 246 public int getStateB() 247 { 248 return stateB; 249 } 250 251 /** 252 * Sets the second part of the state to the given int. 253 * @param stateB any int 254 */ 255 public void setStateB(int stateB) 256 { 257 this.stateB = stateB; 258 } 259 260 /** 261 * Sets the current internal state of this SilkRNG with two ints, where stateA and stateB can each be any int. 262 * @param stateA any int 263 * @param stateB any int 264 */ 265 public void setState(int stateA, int stateB) 266 { 267 this.stateA = stateA; 268 this.stateB = stateB; 269 } 270 271 /** 272 * Get the current internal state of the StatefulRandomness as a long. 273 * 274 * @return the current internal state of this object. 275 */ 276 @Override 277 public long getState() { 278 return stateA & 0xFFFFFFFFL | ((long)stateB) << 32; 279 } 280 281 /** 282 * Set the current internal state of this StatefulRandomness with a long. 283 * 284 * @param state a 64-bit long. You should avoid passing 0; this implementation will treat it as 1. 285 */ 286 @Override 287 public void setState(final long state) { 288 stateA = (int)(state & 0xFFFFFFFFL); 289 stateB = (int)(state >>> 32); 290 } 291 292 @Override 293 public boolean equals(Object o) { 294 if (this == o) return true; 295 if (o == null || getClass() != o.getClass()) return false; 296 297 SilkRNG silkRNG = (SilkRNG) o; 298 299 if (stateA != silkRNG.stateA) return false; 300 return stateB == silkRNG.stateB; 301 } 302 303 @Override 304 public int hashCode() { 305 return 31 * stateA + stateB; 306 } 307 308 @Override 309 public String toString() { 310 return "SilkRNG with stateA 0x" + StringKit.hex(stateA) + " and stateB 0x" + StringKit.hex(stateB); 311 } 312 313 /** 314 * A deterministic random int generator that, given one int {@code state} as input, irreversibly returns an 315 * almost-always-different int as a result. Unlike the rest of SilkRNG, this will not produce all possible ints given 316 * all ints as inputs, and probably a third of all possible ints cannot be returned. You should call this with 317 * {@code SilkRNG.determineInt(state = state + 1 | 0)} (you can subtract 1 to go backwards instead of forwards), 318 * which will allow overflow in the incremented state to be handled the same on GWT as on desktop. 319 * @param state an int that should go up or down by 1 each call, as with {@code SilkRNG.determineInt(state = state + 1 | 0)} to handle overflow 320 * @return a not-necessarily-unique int that is usually very different from {@code state} 321 */ 322 public static int determineInt(int state) { 323 return (state = ((state = (state ^ 0xD1B54A35) * 0x102473) ^ state >>> 11 ^ state >>> 21) * (state | 0xFFE00001)) ^ state >>> 13 ^ state >>> 19; 324 } 325 326 /** 327 * A deterministic random int generator that, given one int {@code state} and an outer int {@code bound} as input, 328 * returns an int between 0 (inclusive) and {@code bound} (exclusive) as a result, which should have no noticeable 329 * correlation between {@code state} and the result. You should call this with 330 * {@code SilkRNG.determineBound(state = state + 1 | 0, bound)} (you can subtract 1 to go backwards instead of 331 * forwards), which will allow overflow in the incremented state to be handled the same on GWT as on desktop. 332 * Like most bounded int generation in SquidLib, this uses some long math, but most of the function uses ints. 333 * @param state an int that should go up or down by 1 each call, as with {@code SilkRNG.determineBounded(state = state + 1 | 0, bound)} to handle overflow 334 * @param bound the outer exclusive bound, as an int; may be positive or negative 335 * @return an int between 0 (inclusive) and {@code bound} (exclusive) 336 */ 337 public static int determineBounded(int state, final int bound) 338 { 339 return (int) ((((state = ((state = (state ^ 0xD1B54A35) * 0x102473) ^ state >>> 11 ^ state >>> 21) * (state | 0xFFE00001)) ^ state >>> 13 ^ state >>> 19) & 0xFFFFFFFFL) * bound >> 32); 340 } 341 /** 342 * A deterministic random long generator that, given one int {@code state} as input, returns an 343 * almost-always-different long as a result. This can only return a tiny fraction of all possible longs, since there 344 * are at most 2 to the 32 possible ints and this doesn't even return different values for each of those. You should 345 * call this with {@code SilkRNG.determine(state = state + 1 | 0)} (you can subtract 1 to go backwards instead of 346 * forwards), which will allow overflow in the incremented state to be handled the same on GWT as on desktop. 347 * @param state an int that should go up or down by 1 each call, as with {@code SilkRNG.determine(state = state + 1 | 0)} to handle overflow 348 * @return a not-necessarily-unique long that is usually very different from {@code state} 349 */ 350 public static long determine(int state) 351 { 352 int r = (state ^ 0xD1B54A35) * 0x102473; 353 return ((long) ((r = (r ^ r >>> 11 ^ r >>> 21) * (r | 0xFFE00001)) ^ r >>> 13 ^ r >>> 19) << 32) 354 | (((state = ((state = (r ^ 0xD1B54A35) * 0x102473) ^ state >>> 11 ^ state >>> 21) * (state | 0xFFE00001)) ^ state >>> 13 ^ state >>> 19) & 0xFFFFFFFFL); 355 } 356 /** 357 * A deterministic random float generator that, given one int {@code state} as input, returns an 358 * almost-always-different float between 0.0f and 1.0f as a result. Unlike the rest of SilkRNG, this might not 359 * produce all possible floats given all ints as inputs, and some fraction of possible floats cannot be returned. 360 * You should call this with {@code SilkRNG.determineFloat(state = state + 1 | 0)} (you can subtract 1 to go 361 * backwards instead of forwards), which will allow overflow in the incremented state to be handled the same on GWT 362 * as on desktop. 363 * @param state an int that should go up or down by 1 each call, as with {@code SilkRNG.determineFloat(state = state + 1 | 0)} to handle overflow 364 * @return a not-necessarily-unique float from 0.0f to 1.0f that is usually very different from {@code state} 365 */ 366 public static float determineFloat(int state) { 367 return (((state = ((state = (state ^ 0xD1B54A35) * 0x102473) ^ state >>> 11 ^ state >>> 21) * (state | 0xFFE00001)) ^ state >>> 13 ^ state >>> 19) & 0xFFFFFF) * 0x1p-24f; 368 } 369 /** 370 * A deterministic random double generator that, given one int {@code state} as input, returns an 371 * almost-always-different double between 0.0 and 1.0 as a result. This cannot produce more than a tiny fraction of 372 * all possible doubles because the input is 32 bits and at least 53 bits are needed to represent most doubles from 373 * 0.0 to 1.0. You should call this with {@code SilkRNG.determineDouble(state = state + 1 | 0)} (you can subtract 1 374 * to go backwards instead of forwards), which will allow overflow in the incremented state to be handled the same 375 * on GWT as on desktop. 376 * @param state an int that should go up or down by 1 each call, as with {@code SilkRNG.determineDouble(state = state + 1 | 0)} to handle overflow 377 * @return a not-necessarily-unique double from 0.0 to 1.0 that is usually very different from {@code state} 378 */ 379 public static double determineDouble(int state) 380 { 381 return ((state = ((state = (state ^ 0xD1B54A35) * 0x102473) ^ state >>> 11 ^ state >>> 21) * (state | 0xFFE00001)) ^ state >>> 13 ^ state >>> 19) * 0x1p-32 + 0.5; 382 } 383}