package squidpony.squidmath; import java.util.Arrays; /** * This is a port of the public domain Isaac (cryptographic) random number generator to Java, by Bob Jenkins. * It is a RandomnessSource here, so it should generally be used to make an RNG, which has more features. * Isaac32RNG is slower than the non-cryptographic RNGs in SquidLib, but much faster than cryptographic RNGs * that need SecureRandom, and it's compatible with GWT and Android to boot! Isaac32RNG should perform better * than IsaacRNG on GWT, or when you specifically need a large amount of int values to be set using * {@link #setBlock(int[])}. If you don't need GWT support, then {@link IsaacRNG} will have better properties. * Created by Tommy Ettinger on 8/1/2016. */ public class Isaac32RNG implements RandomnessSource { final static int SIZEL = 8; /* log of size of results[] and mem[] */ final static int SIZE = 1 << SIZEL; /* size of results[] and mem[] */ // 256 final static int MASK = (SIZE - 1) << 2; /* for pseudorandom lookup */ // 1020 int count; /* count through the results in results[] */ int results[]; /* the results given to the user */ private int mem[]; /* the internal state */ private int a; /* accumulator */ private int b; /* the last result */ private int c; /* counter, guarantees cycle is at least 2^^40 */ /* no seed, equivalent to randinit(ctx,FALSE) in C */ public Isaac32RNG() { mem = new int[SIZE]; results = new int[SIZE]; init(false); } /* equivalent to randinit(ctx, TRUE) after putting seed in randctx in C */ public Isaac32RNG(final int seed[]) { mem = new int[SIZE]; results = new int[SIZE]; if(seed == null) init(false); else { System.arraycopy(seed, 0, results, 0, Math.min(256, seed.length)); init(true); } } /** * Constructs an IsaacRNG with its state filled by the value of seed, run through the LightRNG algorithm. * @param seed any long; will have equal influence on all bits of state */ public Isaac32RNG(long seed) { mem = new int[SIZE]; results = new int[SIZE]; long z; for (int i = 0; i < 256; i++) { z = seed += 0x9E3779B97F4A7C15L; z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; results[i++] = (int) ((z ^ (z >>> 31)) & 0xffffffffL); results[i] = (int) ((z ^ (z >>> 31)) >>> 32); } init(true); } /** * Constructs an IsaacRNG with its state filled by repeated hashing of seed. * @param seed a String that should be exceptionally long to get the best results. */ public Isaac32RNG(String seed) { mem = new int[SIZE]; results = new int[SIZE]; if(seed == null) init(false); else { char[] chars = seed.toCharArray(); int slen = chars.length, i = 0; for (; i < 256 && i < slen; i++) { results[i] = CrossHash.Wisp.hash(chars, i, slen); } for (; i < 256; i++) { results[i] = CrossHash.Wisp.hash(results); } init(true); } } private Isaac32RNG(Isaac32RNG other) { this(other.results); } /** * Generates 256 results to be used by later calls to next() or nextLong(). * This is a fast (not small) implementation. */ public final void regen() { int i, j, x, y; b += ++c; for (i = 0, j = SIZE >>> 1; i < SIZE >>> 1; ) { x = mem[i]; a ^= a << 13; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; x = mem[i]; a ^= a >>> 6; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; x = mem[i]; a ^= a << 2; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; x = mem[i]; a ^= a >>> 16; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; } for (j = 0; j < SIZE >>> 1; ) { x = mem[i]; a ^= a << 13; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; x = mem[i]; a ^= a >>> 6; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; x = mem[i]; a ^= a << 2; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; x = mem[i]; a ^= a >>> 16; a += mem[j++]; mem[i] = y = mem[(x & MASK) >> 2] + a + b; results[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x; } } /** * Initializes this IsaacRNG; typically used from the constructor but can be called externally. * * @param flag if true, use data from seed; if false, initializes this to unseeded random state */ public final void init(boolean flag) { int i; int a, b, c, d, e, f, g, h; a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */ for (i = 0; i < 4; ++i) { a ^= b << 11; d += a; b += c; b ^= c >>> 2; e += b; c += d; c ^= d << 8; f += c; d += e; d ^= e >>> 16; g += d; e += f; e ^= f << 10; h += e; f += g; f ^= g >>> 4; a += f; g += h; g ^= h << 8; b += g; h += a; h ^= a >>> 9; c += h; a += b; } for (i = 0; i < SIZE; i += 8) { /* fill in mem[] with messy stuff */ if (flag) { a += results[i]; b += results[i + 1]; c += results[i + 2]; d += results[i + 3]; e += results[i + 4]; f += results[i + 5]; g += results[i + 6]; h += results[i + 7]; } a ^= b << 11; d += a; b += c; b ^= c >>> 2; e += b; c += d; c ^= d << 8; f += c; d += e; d ^= e >>> 16; g += d; e += f; e ^= f << 10; h += e; f += g; f ^= g >>> 4; a += f; g += h; g ^= h << 8; b += g; h += a; h ^= a >>> 9; c += h; a += b; mem[i] = a; mem[i + 1] = b; mem[i + 2] = c; mem[i + 3] = d; mem[i + 4] = e; mem[i + 5] = f; mem[i + 6] = g; mem[i + 7] = h; } if (flag) { /* second pass makes all of seed affect all of mem */ for (i = 0; i < SIZE; i += 8) { a += mem[i]; b += mem[i + 1]; c += mem[i + 2]; d += mem[i + 3]; e += mem[i + 4]; f += mem[i + 5]; g += mem[i + 6]; h += mem[i + 7]; a ^= b << 11; d += a; b += c; b ^= c >>> 2; e += b; c += d; c ^= d << 8; f += c; d += e; d ^= e >>> 16; g += d; e += f; e ^= f << 10; h += e; f += g; f ^= g >>> 4; a += f; g += h; g ^= h << 8; b += g; h += a; h ^= a >>> 9; c += h; a += b; mem[i] = a; mem[i + 1] = b; mem[i + 2] = c; mem[i + 3] = d; mem[i + 4] = e; mem[i + 5] = f; mem[i + 6] = g; mem[i + 7] = h; } } regen(); count = SIZE; } public final int nextInt() { if (0 == count--) { regen(); count = SIZE - 1; } return results[count]; } /** * Generates and returns a block of 256 pseudo-random int values. * @return a freshly-allocated array of 256 pseudo-random ints, with all bits possible */ public final int[] nextBlock() { regen(); final int[] block = new int[SIZE]; System.arraycopy(results, 0, block, 0, SIZE); count = 0; return block; } /** * Generates enough pseudo-random int values to fill {@code data} and assigns them to it. */ public final void setBlock(final int[] data) { int len, i; if(data == null || (len = data.length) == 0) return; for (i = 0; len > 256; i += 256, len -= 256) { regen(); System.arraycopy(results, 0, data, i, 256); } regen(); System.arraycopy(results, 0, data, i, len); count = len & 255; } /** * Generates enough pseudo-random float values between 0f and 1f to fill {@code data} and assigns them to it. * Inclusive on 0f, exclusive on 1f. Intended for cases where you need some large source of randomness to be checked * later repeatedly, such as how permutation tables are used in Simplex noise. */ public final void setBlock(final float[] data) { int len, n; if(data == null || (len = data.length) == 0) return; for (int i = 0; i < len; i++) { data[i] = NumberTools.intBitsToFloat(((n = nextInt()) >>> 9 ^ (n & 0x7fffff)) | 0x3f800000) - 1f; } } /** * Generates enough pseudo-random float values between -1f and 1f to fill {@code data} and assigns them to it. * Inclusive on -1f, exclusive on 1f. Intended for cases where you need some large source of randomness to be * checked later repeatedly, such as how permutation tables are used in Simplex noise. */ public final void setSignedBlock(final float[] data) { int len, n; if(data == null || (len = data.length) == 0) return; for (int i = 0; i < len; i++) { data[i] = NumberTools.intBitsToFloat(((n = nextInt()) >>> 9 ^ (n & 0x7fffff)) | 0x40000000) - 3f; } } @Override public final int next( int bits ) { return nextInt() >>> 32 - bits; } /** * Using this method, any algorithm that needs to efficiently generate more * than 32 bits of random data can interface with this randomness source. * <p> * Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive). * * @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive) */ @Override public long nextLong() { return (nextInt() & 0xffffffffL) | (nextInt() & 0xffffffffL) << 32; } /** * Produces another RandomnessSource, but the new one will not produce the same data as this one. * This is meant to be a "more-secure" generator, so this helps reduce the ability to guess future * results from a given sequence of output. * @return another RandomnessSource with the same implementation but no guarantees as to generation */ @Override public RandomnessSource copy() { return new Isaac32RNG(results); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Isaac32RNG isaac32RNG = (Isaac32RNG) o; if (count != isaac32RNG.count) return false; if (a != isaac32RNG.a) return false; if (b != isaac32RNG.b) return false; if (c != isaac32RNG.c) return false; if (!Arrays.equals(results, isaac32RNG.results)) return false; return Arrays.equals(mem, isaac32RNG.mem); } @Override public int hashCode() { int result = count; result = 31 * result + CrossHash.Wisp.hash(results); result = 31 * result + CrossHash.Wisp.hash(mem); result = 31 * result + a; result = 31 * result + b; result = 31 * result + c; return result; } @Override public String toString() { return "Isaac32RNG with a hidden state (id is " + System.identityHashCode(this) + ')'; } }