001package squidpony.squidmath;
002
003import java.io.Serializable;
004import java.util.Arrays;
005import java.util.Random;
006
007/**
008 * A low-quality but very fast RNG that has no apparent visual artifacts here; uses Mark Overton's CMR subcycle
009 * generator type, but modified to be especially GWT-friendly. Even though it has no visual issues when rendered as
010 * pixels, it still fails PractRand testing almost immediately. This is meant to be an answer to when people ask for
011 * a bare-minimum generator that's still "good enough" for games. It has a period of 0xFFF43787 or 4294195079, which
012 * can be exhausted in seconds if you only generate numbers in that time, but some seeds will be in a different
013 * cycle with a much lower period. The likelihood of choosing one of these seeds is low, less than a fiftieth of one
014 * percent, but it can happen. It cannot produce all possible ints in its longest cycle, and it can't produce even a
015 * fraction of all possible ints in its smallest cycle. It implements RandomnessSource, but if you just want to copy
016 * this class with no dependencies, then the class declaration can easily be changed to
017 * {@code public class BasicRandom32 extends Random implements Serializable} without any other changes. Note, it does
018 * extend java.util.Random for additional ease of integration, but doesn't use the slow {@code synchronized} keyword
019 * that Random's implementations do.
020 * <br>
021 * <a href="http://www.drdobbs.com/tools/fast-high-quality-parallel-random-number/231000484">This Dr. Dobb's article has
022 * more on this type of generator</a>.
023 * @author Mark Overton
024 * @author Tommy Ettinger
025 */
026public class BasicRandom32 extends Random implements RandomnessSource, Serializable {
027    private static final long serialVersionUID = 1L;
028    public int state;
029
030    public BasicRandom32()
031    {
032        state = 1;
033    }
034
035    public BasicRandom32(final int seed) {
036        setState(seed);
037    }
038
039    public void setState(final int seed)
040    {
041        state = (seed ^ 0x41C64E6D) * 0x9E373 ^ (seed >>> 16);
042    }
043
044    public final long nextLong() {
045        int y = state * 0xBCFD;
046        y = (y << 17 | y >>> 15);
047        final int x = y * 0xBCFD;
048        return ((long) y << 32 ^ (state = (x << 17 | x >>> 15)));
049
050    }
051
052    /**
053     * Gets an int with at most the specified amount of bits; don't confuse this with {@link #nextInt(int)}, which gets
054     * a number between 0 and its int argument, where this draws from a different (larger) range of random results. For
055     * example, {@code next(2)} can return any 2-bit int,
056     * which is limited to 0, 1, 2, or 3. Note that if you request 0 bits, this can give you any int (32 bits).
057     * @param bits the number of bits to get, from 1 to 32
058     * @return an int with at most the specified bits
059     */
060    public final int next(final int bits) {
061        final int y = state * 0xBCFD;
062        return (state = (y << 17 | y >>> 15)) >>> (32 - bits);
063    }
064    public final int nextInt() {
065        final int y = state * 0xBCFD;
066        return (state = (y << 17 | y >>> 15));
067    }
068
069    /**
070     * Returns a random non-negative integer between 0 (inclusive) and the given bound (exclusive),
071     * or 0 if the bound is 0. The bound can be negative, which will produce 0 or a negative result.
072     * <br>
073     * Credit goes to Daniel Lemire, http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
074     *
075     * @param bound the outer bound (exclusive), can be negative or positive
076     * @return the found number
077     */
078    public int nextInt(final int bound) {
079        return (int) ((bound * (nextInt() & 0xFFFFFFFFL)) >> 32);
080    }
081    /**
082     * Exclusive on bound (which must be positive), with an inner bound of 0.
083     * If bound is negative or 0 this always returns 0.
084     * <br>
085     * Credit for this method goes to <a href="https://oroboro.com/large-random-in-range/">Rafael Baptista's blog</a>
086     * for the original idea, and the JDK10 Math class' usage of Karatsuba multiplication for the current algorithm. 
087     * This method is drastically faster than the previous implementation when the bound varies often (roughly 4x
088     * faster, possibly more). It also always gets exactly two random ints, so by default it advances the state as much
089     * as {@link #nextLong()}.
090     *
091     * @param bound the outer exclusive bound; should be positive, otherwise this always returns 0L
092     * @return a random long between 0 (inclusive) and bound (exclusive)
093     */
094    public long nextLong(long bound) {
095        final long rand = nextInt() & 0xFFFFFFFFL;
096        final long randLow = nextInt() & 0xFFFFFFFFL;
097        if (bound <= 0) return 0;
098        final long boundLow = bound & 0xFFFFFFFFL;
099        bound >>>= 32;
100        final long a = rand * bound;
101        final long b = randLow * boundLow;
102        return (((b >>> 32) + (rand + randLow) * (bound + boundLow) - a - b) >>> 32) + a;
103    }
104
105    /**
106     * Sets the seed using a long, by XORing the upper and lower halves of {@code seed} and passing that to
107     * {@link #setState(int)}.
108     * @param seed the initial seed
109     */
110    @Override
111    public void setSeed(long seed) {
112        setState((int)(seed ^ seed >>> 32));
113    }
114    /**
115     * Mutates the array arr by switching the contents at pos1 and pos2.
116     * @param arr an array of T; must not be null
117     * @param pos1 an index into arr; must be at least 0 and no greater than arr.length
118     * @param pos2 an index into arr; must be at least 0 and no greater than arr.length
119     */
120    protected static <T> void swap(T[] arr, int pos1, int pos2) {
121        final T tmp = arr[pos1];
122        arr[pos1] = arr[pos2];
123        arr[pos2] = tmp;
124    }
125
126    /**
127     * Shuffle an array using the Fisher-Yates algorithm and returns a shuffled copy, freshly-allocated, without
128     * modifying elements.
129     * <br>
130     * <a href="https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle">Wikipedia has more on this algorithm</a>.
131     *
132     * @param elements an array of T; will not be modified
133     * @return a shuffled copy of elements
134     */
135    public <T> T[] shuffle(T[] elements) {
136        final int size = elements.length;
137        final T[] array = Arrays.copyOf(elements, size);
138        for (int i = size; i > 1; i--) {
139            swap(array, i - 1, nextInt(i));
140        }
141        return array;
142    }
143
144    /**
145     * Shuffles an array in-place using the Fisher-Yates algorithm, affecting indices from 0 (inclusive) to length
146     * (exclusive). May be useful with libGDX Array instances, which can be shuffled with
147     * {@code random.shuffleInPlace(arr.items, arr.size)}. If you don't want the array modified, use
148     * {@link #shuffle(Object[])}.
149     * <br>
150     * <a href="https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle">Wikipedia has more on this algorithm</a>.
151     *
152     * @param elements an array of T; <b>will</b> be modified
153     * @return elements after shuffling it in-place
154     */
155    public <T> T[] shuffleInPlace(T[] elements, int length) {
156        final int size = Math.min(elements.length, length);
157        for (int i = size; i > 1; i--) {
158            swap(elements, i - 1, nextInt(i));
159        }
160        return elements;
161    }
162
163    /**
164     * Shuffles an array in-place using the Fisher-Yates algorithm.
165     * If you don't want the array modified, use {@link #shuffle(Object[])}.
166     * <br>
167     * <a href="https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle">Wikipedia has more on this algorithm</a>.
168     *
169     * @param elements an array of T; <b>will</b> be modified
170     * @return elements after shuffling it in-place
171     */
172    public <T> T[] shuffleInPlace(T[] elements) {
173        final int size = elements.length;
174        for (int i = size; i > 1; i--) {
175            swap(elements, i - 1, nextInt(i));
176        }
177        return elements;
178    }
179
180    public BasicRandom32 copy() {
181        BasicRandom32 sr = new BasicRandom32();
182        sr.state = state;
183        return sr;
184    }
185}