001package squidpony.squidmath;
002
003import squidpony.Maker;
004
005import java.io.Serializable;
006import java.util.*;
007
008/**
009 * Meant to take a fixed-size set of items and produce a shuffled stream of them such that an element is never chosen in
010 * quick succession; that is, there should always be a gap between the same item's occurrences. This is an Iterable of
011 * T, not a Collection, because it can iterate without stopping, infinitely, unless you break out of a foreach loop that
012 * iterates through one of these, or call the iterator's next() method only a limited number of times. Collections have
013 * a size that can be checked, but Iterables can be infinite (and in this case, this one is).
014 * Created by Tommy Ettinger on 5/21/2016.
015 * @param <T> the type of items to iterate over; ideally, the items are unique
016 */
017public class GapShuffler<T> implements Iterator<T>, Iterable<T>, Serializable {
018    private static final long serialVersionUID = 1L;
019    public IRNG rng;
020    private ArrayList<T> elements;
021    private int index;
022    private GapShuffler() {}
023
024    public GapShuffler(T single)
025    {
026        rng = new RNG();
027        elements = new ArrayList<>(1);
028        elements.add(single);
029        index = 0;
030    }
031    /**
032     * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely
033     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
034     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
035     * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works.
036     * @param elements a Collection of T that will not be modified
037     */
038    public GapShuffler(Collection<T> elements)
039    {
040        this(elements, new RNG(new LongPeriodRNG()));
041
042    }
043
044    /**
045     * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely
046     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
047     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
048     * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works.
049     * @param elements a Collection of T that will not be modified
050     */
051    public GapShuffler(Collection<T> elements, String seed)
052    {
053        this(elements, new RNG(new LongPeriodRNG(seed)));
054    }
055
056    /**
057     * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely
058     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
059     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
060     * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The
061     * rng parameter is copied so externally using it won't change the order this produces its values; the rng field is
062     * used whenever the iterator needs to re-shuffle the internal ordering of items. I suggest that the IRNG should
063     * use {@link LongPeriodRNG} as its RandomnessSource, since it is in general a good choice for shuffling, or
064     * {@link XoshiroStarPhi32RNG} for GWT or other 32-bit platforms, but the choice is unlikely to matter in practice.
065     * @param items a Collection of T that will not be modified
066     * @param rng an IRNG that can be pre-seeded; will be copied and not used directly
067     */
068    public GapShuffler(Collection<T> items, IRNG rng)
069    {
070        this.rng = rng.copy();
071        elements = this.rng.shuffle(items);
072        index = 0;
073    }
074
075    /**
076     * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely
077     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
078     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
079     * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The
080     * rng parameter will be copied if {@code shareRNG} is true, otherwise the reference will be shared (which could
081     * make the results of this GapShuffler depend on outside code, though it will always maintain a gap between
082     * identical elements if the elements are unique). I suggest that the IRNG should use {@link LongPeriodRNG} as its
083     * RandomnessSource, since it is in general a good choice for shuffling, or {@link XoshiroStarPhi32RNG} for GWT or
084     * other 32-bit platforms, but the choice is unlikely to matter in practice. Any {@link IStatefulRNG} should work in
085     * most cases, like {@link GWTRNG}, {@link StatefulRNG}, or {@link SilkRNG}. If you encounter unusually-low-quality
086     * shuffles, try a different starting seed, and if that doesn't work, try SilkRNG (it has a different internal
087     * structure than the other types; it could yield better results at the very start).
088     * @param items a Collection of T that will not be modified
089     * @param rng an IRNG that can be pre-seeded; will be copied and not used directly
090     * @param shareRNG if false, {@code rng} will be copied and no reference will be kept; if true, {@code rng} will be shared with the outside code
091     */
092    public GapShuffler(Collection<T> items, IRNG rng, boolean shareRNG)
093    {
094        this.rng = shareRNG ? rng : rng.copy();
095        elements = this.rng.shuffle(items);
096        index = 0;
097    }
098
099    /**
100     * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely
101     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
102     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
103     * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works.
104     * @param elements a Collection of T that will not be modified
105     */
106    public GapShuffler(T[] elements)
107    {
108        this(elements, new RNG(new LongPeriodRNG()));
109    }
110
111    /**
112     * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely
113     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
114     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
115     * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works.
116     * @param elements a Collection of T that will not be modified
117     */
118    public GapShuffler(T[] elements, CharSequence seed)
119    {
120        this(elements, new RNG(new LongPeriodRNG(seed)));
121    }
122
123    /**
124     * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely
125     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
126     * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance
127     * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The
128     * rng parameter is copied so externally using it won't change the order this produces its values; the rng field is
129     * used whenever the iterator needs to re-shuffle the internal ordering of items. I suggest that the IRNG should
130     * use {@link LongPeriodRNG} as its RandomnessSource, since it is in general a good choice for shuffling, or
131     * {@link XoshiroStarPhi32RNG} for GWT or other 32-bit platforms, but the choice is unlikely to matter in practice.
132     * @param items a Collection of T that will not be modified
133     * @param rng an IRNG that can be pre-seeded; will be copied and not used directly
134     */
135    public GapShuffler(T[] items, IRNG rng)
136    {
137        this.rng = rng.copy();
138        elements = Maker.makeList(items);
139        this.rng.shuffleInPlace(elements);
140        index = 0;
141    }
142
143    /**
144     * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely
145     * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should
146     * always have at least one "gap" element between one appearance and the next. It helps to keep the appearance
147     * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The
148     * rng parameter will be copied if {@code shareRNG} is false, otherwise the reference will be shared (which could
149     * make the results of this GapShuffler depend on outside code, though it will always maintain a gap between
150     * identical elements if the elements are unique). I suggest that the IRNG should use {@link LongPeriodRNG} as its
151     * RandomnessSource, since it is in general a good choice for shuffling, or {@link XoshiroStarPhi32RNG} for GWT or
152     * other 32-bit platforms, but the choice is unlikely to matter in practice. Any {@link IStatefulRNG} should work in
153     * most cases, like {@link GWTRNG}, {@link StatefulRNG}, or {@link SilkRNG}. If you encounter unusually-low-quality
154     * shuffles, try a different starting seed, and if that doesn't work, try SilkRNG (it has a different internal
155     * structure than the other types; it could yield better results at the very start).
156     * @param items a Collection of T that will not be modified
157     * @param rng an IRNG that can be pre-seeded; will be copied and not used directly
158     * @param shareRNG if false, {@code rng} will be copied and no reference will be kept; if true, {@code rng} will be shared with the outside code
159     */
160    public GapShuffler(T[] items, IRNG rng, boolean shareRNG)
161    {
162        this.rng = shareRNG ? rng : rng.copy();
163        elements = Maker.makeList(items);
164        this.rng.shuffleInPlace(elements);
165        index = 0;
166    }
167
168    /**
169     * Gets the next element of the infinite sequence of T this shuffles through. This class can be used as an
170     * Iterator or Iterable of type T.
171     * @return the next element in the infinite sequence
172     */
173    public T next() {
174        int size = elements.size();
175        if(size == 1)
176        {
177            return elements.get(0);
178        }
179        if(index >= size)
180        {
181            final int n = size - 1;
182            for (int i = n; i > 1; i--) {
183                Collections.swap(elements, rng.nextSignedInt(i), i - 1);
184            }
185            Collections.swap(elements, 1 + rng.nextSignedInt(n), n);
186            index = 0;
187        }
188        return elements.get(index++);
189    }
190    /**
191     * Returns {@code true} if the iteration has more elements.
192     * This is always the case for GapShuffler.
193     *
194     * @return {@code true} always
195     */
196    @Override
197    public boolean hasNext() {
198        return true;
199    }
200    /**
201     * Not supported.
202     *
203     * @throws UnsupportedOperationException always throws this exception
204     */
205    @Override
206    public void remove() {
207        throw new UnsupportedOperationException("remove() is not supported");
208    }
209
210    /**
211     * Returns an <b>infinite</b> iterator over elements of type {@code T}; the returned iterator is this object.
212     * You should be prepared to break out of any for loops that use this once you've gotten enough elements!
213     * The remove() method is not supported by this iterator and hasNext() will always return true.
214     *
215     * @return an infinite Iterator over elements of type T.
216     */
217    @Override
218    public Iterator<T> iterator() {
219        return this;
220    }
221
222    public IRNG getRNG() {
223        return rng;
224    }
225
226
227    /**
228     * Sets the IRNG this uses to shuffle the order of elements, always copying the given IRNG before using it. Always
229     * reshuffles the order, which may eliminate a gap that should have been present, so treat the sequence before and
230     * after like separate GapShuffler objects.
231     * @param rng an IRNG, such as {@link RNG}, {@link GWTRNG}, {@link StatefulRNG}, and so on; always copied
232     */
233    public void setRNG(IRNG rng) {
234        setRNG(rng, false);
235    }
236
237    /**
238     * Sets the IRNG this uses to shuffle the order of elements, optionally sharing a reference between outside code and
239     * the internal rng (when {@code shareRNG} is true). Always reshuffles the order, which may eliminate a gap that
240     * should have been present, so treat the sequence before and after like separate GapShuffler objects.
241     * @param rng an IRNG, such as {@link RNG}, {@link GWTRNG}, {@link StatefulRNG}, and so on
242     * @param shareRNG if false, {@code rng} will be copied and no reference will be kept; if true, {@code rng} will be shared with the outside code
243     */
244    public void setRNG(IRNG rng, boolean shareRNG) {
245        this.rng = shareRNG ? rng : rng.copy();
246        this.rng.shuffleInPlace(elements);
247    }
248
249    /**
250     * The internal items used here are private, but you can still use this method to put a shallow copy of them into
251     * some other Collection. If the type {@code T} is mutable, changes to individual items will be reflected in this
252     * GapShuffler, so be careful in that case (if T is immutable, like if it is String, then there's nothing to need to
253     * be careful about). This copies each item in the GapShuffler's sequence once, in no particular order, but it may
254     * give a prediction of what items this will return in the future (or has already returned).
255     * @param coll a Collection that will have each of the possible items this can produce added into it, in no particular order
256     */
257    public void fillInto(Collection<T> coll) {
258        coll.addAll(elements);
259    }
260    
261}