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}