001package squidpony.squidmath;
002
003import java.io.Serializable;
004
005/**
006 * An infinite sequence of pseudo-random ints (typically used as indices) from 0 to some bound, with all possible ints
007 * returned in a shuffled order before re-shuffling for the next result. Does not store the sequence in memory. Uses a
008 * Feistel network, as described in <a href="https://blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/">this post by Alan Wolfe</a>.
009 * The API is very simple; you construct a ShuffledIntSequence by specifying how many items it should shuffle (the
010 * actual sequence is boundless, but the items it can return are limited to between 0 and some bound), and you can
011 * optionally use a seed (it will be random if you don't specify a seed). Call {@link #next()} on a ShuffledIntSequence
012 * to get the next distinct int in the shuffled ordering; next() will re-shuffle the sequence if it runs out of distinct
013 * possible results. You can go back to the previous item with {@link #previous()}, which is allowed to go earlier than
014 * the first result generated (it will jump back to what is effectively the previous shuffled sequence). You can restart
015 * the sequence with {@link #restart()} to use the same sequence over again (which doesn't make much sense here, since
016 * this makes many sequences by re-shuffling), or {@link #restart(int)} to use a different seed (the bound is fixed).
017 * <br>
018 * This differs from the version in Alan Wolfe's example code and blog post; it uses a very different round function,
019 * and it only uses 2 rounds of it (instead of 4). Wolfe's round function is MurmurHash2, but as far as I can tell the
020 * version he uses doesn't have anything like MurmurHash3's fmix32() to adequately avalanche bits, and since all keys
021 * are small keys with the usage of MurmurHash2 in his code, avalanche is the most important thing. It's also perfectly
022 * fine to use irreversible operations in a Feistel network round function, and I do that since it seems to improve
023 * randomness slightly. The {@link #round(int, int)} method used here acts like SilkRNG's way of mixing two states, but
024 * here changed to be irreversible and unbalanced (which is fine here). Using 4 rounds turns out to be overkill in this
025 * case. This also uses a different seed for each round.
026 * <br>
027 * This class extends {@link LowStorageShuffler}, changing it from producing a unique set of ints once, to producing
028 * many sets of ints one after the next.
029 * <br>
030 * Created by Tommy Ettinger on 9/30/2018.
031 * @author Alan Wolfe
032 * @author Tommy Ettinger
033 */
034public class ShuffledIntSequence extends LowStorageShuffler implements Serializable {
035    private static final long serialVersionUID = 1L;
036    protected int seed;
037    /**
038     * Constructs a ShuffledIntSequence with a random seed and a bound of 10.
039     */
040    public ShuffledIntSequence(){
041        this(10);
042    }
043
044    /**
045     * Constructs a ShuffledIntSequence with the given exclusive upper bound and a random seed.
046     * @param bound how many distinct ints this can return
047     */
048    public ShuffledIntSequence(int bound)
049    {
050        this(bound, (int)((Math.random() * 2.0 - 1.0) * 0x80000000));
051    }
052
053    /**
054     * Constructs a ShuffledIntSequence with the given exclusive upper bound and int seed.
055     * @param bound how many distinct ints this can return
056     * @param seed any int; will be used to get several seeds used internally
057     */
058    public ShuffledIntSequence(int bound, int seed)
059    {
060        super(bound, seed);
061        this.seed = seed;
062    }
063
064    /**
065     * Gets the next distinct int in the sequence, shuffling the sequence if it has been exhausted so it never runs out.
066     * @return the next item in the sequence
067     */
068    @Override
069    public int next()
070    {
071        int r = super.next();
072        if(r == -1)
073        {
074            restart(seed = seed + 0x9E3779B9 | 0);
075            return super.next();
076        }
077        return r;
078    }
079    /**
080     * Gets the previous returned int from the sequence (as yielded by {@link #next()}), restarting the sequence in a
081     * correctly-ordered way if it would go to before the "start" of the sequence (it is actually close to infinite both
082     * going forwards and backwards).
083     * @return the previously-given item in the sequence, or -1 if something goes wrong (which shouldn't be possible)
084     */
085    @Override
086    public int previous()
087    {
088        int shuffleIndex;
089        // two tries to get a result, return -1 in the probably-impossible case this fails
090        for (int i = 0; i < 2; i++) {
091            while (index > 0) {
092                // get the next number
093                shuffleIndex = encode(--index);
094
095                // if we found a valid index, return success!
096                if (shuffleIndex < bound)
097                    return shuffleIndex;
098            }
099            restart(seed = seed - 0x9E3779B9 | 0);
100            index = pow4;
101        }
102        return -1;
103    }
104    
105    /**
106     * Starts the sequence over, but can change the seed (completely changing the sequence). If {@code seed} is the same
107     * as the seed given in the constructor, this will use the same sequence, acting like {@link #restart()}.
108     * @param seed any int; will be used to get several seeds used internally
109     */
110    @Override
111    public void restart(int seed)
112    {
113        super.restart(seed);
114        this.seed = seed;
115    }
116}