001package squidpony.squidmath;
002
003import squidpony.ArrayTools;
004
005import java.io.Serializable;
006import java.util.ArrayList;
007import java.util.Iterator;
008import java.util.SortedSet;
009
010/**
011 * An ordered multi-directional map-like data structure with two sets of keys and one list of values.
012 *
013 * This is structured so that the two keys, of types A and B, are always associated with each other and the same value,
014 * of type Q, but may have their index in the ordering change. You can look up a B key with an A key or index, an A key
015 * with a B key or index, and a Q value with an A key, B key, or index.
016 * Created by Tommy Ettinger on 10/27/2016.
017 */
018public class K2V1<A, B, Q> implements Serializable {
019    private static final long serialVersionUID = 1L;
020    public K2<A, B> keys;
021    public ArrayList<Q> values;
022
023    /**
024     * Constructs an empty K2V1 with the default parameters: 32 expected indices and a load factor of 0.5f.
025     */
026    public K2V1()
027    {
028        this(32, 0.5f);
029    }
030
031    /**
032     * Constructs an empty K2V1 that can hold {@code expected} indices before resizing and has a load factor of 0.5f.
033     * @param expected the expected number of items of any single type that this will hold; each put counts as one item
034     */
035    public K2V1(int expected)
036    {
037        this(expected, 0.5f);
038    }
039
040    /**
041     * Constructs an empty K2V1 that can hold {@code expected} indices before resizing and has load factor {@code f}.
042     * @param expected the expected number of items of any single type that this will hold; each put counts as one item
043     * @param f the load factor; must be greater than 0 and less than 1, but should ideally be between 0.2 and 0.8
044     */
045    public K2V1(int expected, float f)
046    {
047        keys = new K2<>(expected, f);
048        values = new ArrayList<>(expected);
049    }
050
051    /**
052     * Constructs a K2V1 from an A iterable, a B iterable, and a Q iterable, where the A and B items should be unique
053     * (if they aren't, each item that would be associated with a duplicate A or B will be skipped). The K2V1 this
054     * constructs will only take enough items from all Iterables to exhaust the shortest Iterable, so the lengths can be
055     * different between arguments. If there are no duplicate A keys or duplicate B keys (e.g. you can have an A key
056     * that is equal to a B key, but not to another A key), then all items will be used in the order the Iterable
057     * parameters provide them; otherwise the keys and value that would be associated with a duplicate are skipped.
058     * @param aKeys an Iterable of A keys; if null will be considered empty and this K2V1 will be empty
059     * @param bKeys an Iterable of B keys; if null will be considered empty and this K2V1 will be empty
060     * @param qValues an Iterable of Q values; if null will be considered empty and this K2V1 will be empty
061     */
062    public K2V1(Iterable<A> aKeys, Iterable<B> bKeys, Iterable<Q> qValues)
063    {
064        this(32, 0.5f);
065        putAll(aKeys, bKeys, qValues);
066    }
067
068    /**
069     * Constructs a K2V1 from another K2V1 with ths same A, B, and Q types. This will have an expected size equal to the
070     * current size of other, use the same load factor (f) as other, and will have the same items put into it in the
071     * same order.
072     * @param other a K2V1 to copy into this; must have the same A, B, and Q types
073     */
074    public K2V1(K2V1<A, B, Q> other)
075    {
076        this(other == null ? 32 : other.size(), other == null ? 0.5f : other.keys.keysA.f);
077        putAll(other);
078    }
079    /**
080     * Returns true if this contains the A, key, in its collection of A items, or false otherwise.
081     * @param key the A to check the presence of
082     * @return true if key is present in this; false otherwise
083     */
084    public boolean containsA(A key) { return keys.containsA(key); }
085    /**
086     * Returns true if this contains the B, key, in its collection of B items, or false otherwise.
087     * @param key the B to check the presence of
088     * @return true if key is present in this; false otherwise
089     */
090    public boolean containsB(B key) { return keys.containsB(key); }
091
092    /**
093     * Returns true if this contains at least one Q value, or false otherwise
094     * @param value the value to check
095     * @return true if value is present at least once in this collection's Q collection
096     */
097    public boolean containsQ(Q value) { return values.contains(value); }
098
099    /**
100     * Returns true if index is between 0 (inclusive) and {@link #size()} (exclusive), or false otherwise.
101     * @param index the index to check
102     * @return true if index is a valid index in the ordering of this K2V1
103     */
104    public boolean containsIndex(int index) { return keys.containsIndex(index); }
105
106    /**
107     * Given an A object key, finds the position in the ordering which that A has, or -1 if key is not present.
108     * Unlike {@link java.util.List#indexOf(Object)}, this runs in constant time.
109     * @param key the A to find the position of
110     * @return the int index of key in the ordering, or -1 if it is not present
111     */
112    public int indexOfA(Object key)
113    {
114        return keys.indexOfA(key);
115    }
116    /**
117     * Given a B object key, finds the position in the ordering which that B has, or -1 if key is not present.
118     * Unlike {@link java.util.List#indexOf(Object)}, this runs in constant time.
119     * @param key the B to find the position of
120     * @return the int index of key in the ordering, or -1 if it is not present
121     */
122    public int indexOfB(Object key)
123    {
124        return keys.indexOfB(key);
125    }
126
127    /**
128     * Given a Q value, finds the first position in the ordering which contains that value, or -1 if not present.
129     * Runs in linear time normally, since this uses {@link ArrayList#indexOf(Object)} to search the values.
130     * @param value the value to find the position of, which should be a Q
131     * @return the first int index of value in the ordering, or -1 if it is not present
132     */
133    public int indexOfQ(Object value) {
134        //noinspection SuspiciousMethodCalls
135        return values.indexOf(value);
136    }
137
138    /**
139     * Given an int index, finds the associated A key (using index as a point in the ordering).
140     * @param index an int index into this K2V1
141     * @return the A object with index for its position in the ordering, or null if index was invalid
142     */
143    public A getAAt(int index)
144    {
145        return keys.getAAt(index);
146    }
147    /**
148     * Given an int index, finds the associated B key (using index as a point in the ordering).
149     * @param index an int index into this K2V1
150     * @return the B object with index for its position in the ordering, or null if index was invalid
151     */
152    public B getBAt(int index)
153    {
154        return keys.getBAt(index);
155    }
156
157    /**
158     * Given an int index, finds the associated Q value (using index as a point in the ordering).
159     * @param index an int index into this K2V1
160     * @return the Q value with index for its position in the ordering, or null if index was invalid
161     */
162    public Q getQAt(int index)
163    {
164        if(index < 0 || index >= keys.keysA.size)
165            return null;
166        return values.get(index);
167    }
168
169    /**
170     * Given an A object, finds the associated B object (it will be at the same point in the ordering).
171     * @param key an A object to use as a key
172     * @return the B object associated with key, or null if key was not present
173     */
174    public B getBFromA(Object key)
175    {
176        return keys.getBFromA(key);
177    }
178
179    /**
180     * Given a B object, finds the associated A object (it will be at the same point in the ordering).
181     * @param key a B object to use as a key
182     * @return the A object associated with key, or null if key was not present
183     */
184    public A getAFromB(Object key)
185    {
186        return keys.getAFromB(key);
187    }
188    /**
189     * Given an A object, finds the associated Q value (it will be at the same point in the ordering).
190     * @param key an A object to use as a key
191     * @return the Q value associated with key, or null if key was not present
192     */
193    public Q getQFromA(Object key)
194    {
195        int idx = keys.indexOfA(key);
196        if(idx >= 0)
197            return values.get(idx);
198        return null;
199    }
200    /**
201     * Given a B object, finds the associated Q value (it will be at the same point in the ordering).
202     * @param key a B object to use as a key
203     * @return the Q value associated with key, or null if key was not present
204     */
205    public Q getQFromB(Object key)
206    {
207        int idx = keys.indexOfB(key);
208        if(idx >= 0)
209            return values.get(idx);
210        return null;
211    }
212
213    /**
214     * Gets a random A from this K2V1 using the given IRNG.
215     * @param random generates a random index to get an A with
216     * @return a randomly chosen A, or null if this is empty
217     */
218    public A randomA(IRNG random)
219    {
220        return keys.randomA(random);
221    }
222
223    /**
224     * Gets a random B from this K2V1 using the given IRNG.
225     * @param random generates a random index to get a B with
226     * @return a randomly chosen B, or null if this is empty
227     */
228    public B randomB(IRNG random)
229    {
230        return keys.randomB(random);
231    }
232
233    /**
234     * Gets a random Q from this K2V1 using the given IRNG.
235     * @param random generates a random index to get a Q with
236     * @return a randomly chosen Q, or null if this is empty
237     */
238    public Q randomQ(IRNG random) {
239        if(random == null || values.isEmpty())
240            return null;
241        return values.get(random.nextInt(values.size()));
242    }
243
244    /**
245     * Changes an existing A key, {@code past}, to another A key, {@code future}, if past exists in this K2V1
246     * and future does not yet exist in this K2V1. This will retain past's point in the ordering for future, so
247     * the associated other key(s) will still be associated in the same way.
248     * @param past an A key, that must exist in this K2V1's A keys, and will be changed
249     * @param future an A key, that cannot currently exist in this K2V1's A keys, but will if this succeeds
250     * @return this for chaining
251     */
252    public K2V1<A, B, Q> alterA(A past, A future)
253    {
254        keys.alterA(past, future);
255        return this;
256    }
257
258    /**
259     * Changes an existing B key, {@code past}, to another B key, {@code future}, if past exists in this K2V1
260     * and future does not yet exist in this K2V1. This will retain past's point in the ordering for future, so
261     * the associated other key(s) will still be associated in the same way.
262     * @param past a B key, that must exist in this K2V1's B keys, and will be changed
263     * @param future a B key, that cannot currently exist in this K2V1's B keys, but will if this succeeds
264     * @return this for chaining
265     */
266    public K2V1<A, B, Q> alterB(B past, B future)
267    {
268        keys.alterB(past, future);
269        return this;
270    }
271
272    /**
273     * Changes the A key at {@code index} to another A key, {@code future}, if index is valid and future does not
274     * yet exist in this K2V1. The position in the ordering for future will be the same as index, and the same
275     * as the key this replaced, if this succeeds, so the other key(s) at that position will still be associated in
276     * the same way.
277     * @param index a position in the ordering to change; must be at least 0 and less than {@link #size()}
278     * @param future an A key, that cannot currently exist in this K2V1's A keys, but will if this succeeds
279     * @return this for chaining
280     */
281    public K2V1<A, B, Q> alterAAt(int index, A future)
282    {
283        keys.alterAAt(index, future);
284        return this;
285    }
286
287
288    /**
289     * Changes the B key at {@code index} to another B key, {@code future}, if index is valid and future does not
290     * yet exist in this K2V1. The position in the ordering for future will be the same as index, and the same
291     * as the key this replaced, if this succeeds, so the other key(s) at that position will still be associated in
292     * the same way.
293     * @param index a position in the ordering to change; must be at least 0 and less than {@link #size()}
294     * @param future a B key, that cannot currently exist in this K2V1's B keys, but will if this succeeds
295     * @return this for chaining
296     */
297    public K2V1<A, B, Q> alterBAt(int index, B future)
298    {
299        keys.alterBAt(index, future);
300        return this;
301    }
302
303    /**
304     * Changes the Q value at {@code index} to another Q value, {@code future}, if index is valid. The position in the
305     * ordering for future will be the same as index, and the same as the key this replaced, if this succeeds, so the
306     * keys at that position will still be associated in the same way.
307     * @param index a position in the ordering to change; must be at least 0 and less than {@link #size()}
308     * @param future a Q value that will be set at the given index if this succeeds
309     * @return this for chaining
310     */
311    public K2V1<A, B, Q> alterQAt(int index, Q future)
312    {
313        values.set(index, future);
314        return this;
315    }
316    /**
317     * Adds an A key, a B key, and a Q value at the same point in the ordering (the end) to this K2V1. Neither aKey nor
318     * bKey can be present in this collection before this is called. If you want to change or update an existing key,
319     * use {@link #alterA(Object, Object)} or {@link #alterB(Object, Object)}. The value, qValue, has no restrictions.
320     * @param aKey an A key to add; cannot already be present
321     * @param bKey a B key to add; cannot already be present
322     * @param qValue a Q value to add; can be present any number of times
323     * @return true if this collection changed as a result of this call
324     */
325
326    public boolean put(A aKey, B bKey, Q qValue)
327    {
328        if(keys.put(aKey, bKey))
329        {
330            values.add(qValue);
331            return true;
332        }
333        return false;
334    }
335
336    /**
337     * Adds an A key, a B key, and a Q value at the given index in the ordering to this K2V1. Neither a nor b can be
338     * present in this collection before this is called. If you want to change or update an existing key, use
339     * {@link #alterA(Object, Object)} or {@link #alterB(Object, Object)}. The value, q, has no restrictions. The index
340     * this is given should be at least 0 and no greater than {@link #size()}.
341     * @param index the point in the ordering to place a and b into; later entries will be shifted forward
342     * @param a an A key to add; cannot already be present
343     * @param b a B key to add; cannot already be present
344     * @param q a Q value to add; can be present any number of times
345     * @return true if this collection changed as a result of this call
346     */
347    public boolean putAt(int index, A a, B b, Q q)
348    {
349        if(keys.putAt(index, a, b))
350        {
351            values.add(index, q);
352            return true;
353        }
354        return false;
355    }
356    public boolean putAll(Iterable<A> aKeys, Iterable<B> bKeys, Iterable<Q> qValues)
357    {
358        if(aKeys == null || bKeys == null || qValues == null) return false;
359        Iterator<A> aIt = aKeys.iterator();
360        Iterator<B> bIt = bKeys.iterator();
361        Iterator<Q> qIt = qValues.iterator();
362        boolean changed = false;
363        while (aIt.hasNext() && bIt.hasNext() && qIt.hasNext())
364        {
365            changed = put(aIt.next(), bIt.next(), qIt.next()) || changed;
366        }
367        return changed;
368    }
369    /**
370     * Puts all unique A and B keys in {@code other} into this K2V1, respecting other's ordering. If an A or a B in other
371     * is already present when this would add one, this will not put the A and B keys at that point in the iteration
372     * order, and will place the next unique A and B it finds in the arguments at that position instead.
373     * @param other another K2V1 collection with the same A and B types
374     * @return true if this collection changed as a result of this call
375     */
376    public boolean putAll(K2V1<A, B, Q> other)
377    {
378        if(other == null) return false;
379        boolean changed = false;
380        int sz = other.size();
381        for (int i = 0; i < sz; i++) {
382            changed = put(other.getAAt(i), other.getBAt(i), other.getQAt(i)) || changed;
383        }
384        return changed;
385    }
386    /**
387     * Removes a given A key, if {@code removing} exists in this K2V1's A keys, and also removes any B key or Q value
388     * associated with its point in the ordering.
389     * @param removing the A key to remove
390     * @return this for chaining
391     */
392    public K2V1<A, B, Q> removeA(A removing)
393    {
394        int i = keys.keysA.removeInt(removing);
395        if(i >= 0) {
396            keys.keysB.removeAt(i);
397            values.remove(i);
398        }
399        return this;
400    }
401
402    /**
403     * Removes a given B key, if {@code removing} exists in this K2V1's B keys, and also removes any A key or Q value
404     * associated with its point in the ordering.
405     * @param removing the B key to remove
406     * @return this for chaining
407     */
408    public K2V1<A, B, Q> removeB(B removing)
409    {
410        int i = keys.keysB.removeInt(removing);
411        if(i >= 0) {
412            keys.keysA.removeAt(i);
413            values.remove(i);
414        }
415        return this;
416    }
417    /**
418     * Removes a given point in the ordering, if {@code index} is at least 0 and less than {@link #size()}.
419     * @param index the position in the ordering to remove
420     * @return this for chaining
421     */
422    public K2V1<A, B, Q> removeAt(int index)
423    {
424        if (index >= 0 && index < keys.keysA.size) {
425            keys.removeAt(index);
426            values.remove(index);
427        }
428        return this;
429    }
430
431    /**
432     * Reorders this K2V1 using {@code ordering}, which have the same length as this K2V1's {@link #size()}
433     * and can be generated with {@link ArrayTools#range(int)} (which, if applied, would produce no
434     * change to the current ordering), {@link IRNG#randomOrdering(int)} (which gives a random ordering, and if
435     * applied immediately would be the same as calling {@link #shuffle(IRNG)}), or made in some other way. If you
436     * already have an ordering and want to make a different ordering that can undo the change, you can use
437     * {@link ArrayTools#invertOrdering(int[])} called on the original ordering.
438     * @param ordering an int array or vararg that should contain each int from 0 to {@link #size()} (or less)
439     * @return this for chaining
440     */
441    public K2V1<A, B, Q> reorder(int... ordering)
442    {
443        keys.reorder(ordering);
444        ArrayTools.reorder(values, ordering);
445        return this;
446    }
447
448    /**
449     * Generates a random ordering with rng and applies the same ordering to all keys and values this has; they will
450     * maintain their current association to other keys and values but their ordering/indices will change.
451     * @param rng an IRNG to produce the random ordering this will use
452     * @return this for chaining
453     */
454    public K2V1<A, B, Q> shuffle(IRNG rng)
455    {
456        int[] ordering = rng.randomOrdering(keys.keysA.size);
457        keys.reorder(ordering);
458        ArrayTools.reorder(values, ordering);
459        return this;
460    }
461    /**
462     * Creates a new iterator over the A keys this holds. This can be problematic for garbage collection if called very
463     * frequently; it may be better to access items by index (which also lets you access other keys associated with that
464     * index) using {@link #getAAt(int)} in a for(int i=0...) loop.
465     * @return a newly-created iterator over this K2V1's A keys
466     */
467    public Iterator<A> iteratorA()
468    {
469        return keys.iteratorA();
470    }
471
472    /**
473     * Creates a new iterator over the B keys this holds. This can be problematic for garbage collection if called very
474     * frequently; it may be better to access items by index (which also lets you access other keys associated with that
475     * index) using {@link #getBAt(int)} in a for(int i=0...) loop.
476     * @return a newly-created iterator over this K2V1's B keys
477     */
478    public Iterator<B> iteratorB()
479    {
480        return keys.iteratorB();
481    }
482
483    /**
484     * Creates a new iterator over the Q values this holds. This can be problematic for garbage collection if called
485     * very frequently; it may be better to access values by index (which also lets you access other keys associated
486     * with that index) using {@link #getQAt(int)} in a for(int i=0...) loop.
487     * @return a newly-created iterator over this K2V1's Q values
488     */
489    public Iterator<Q> iteratorQ()
490    {
491        return values.iterator();
492    }
493
494    /**
495     * Gets and caches the A keys as a Collection that implements SortedSet (and so also implements Set). It retains the
496     * current ordering. This Set is shared with this collection; it is not a copy, and changes to the returned Set will
497     * affect this data structure.
498     * @return the A keys as a SortedSet
499     */
500    public SortedSet<A> getSetA()
501    {
502        return keys.getSetA();
503    }
504
505    /**
506     * Gets and caches the B keys as a Collection that implements SortedSet (and so also implements Set). It retains the
507     * current ordering. This Set is shared with this collection; it is not a copy, and changes to the returned Set will
508     * affect this data structure.
509     * @return the B keys as a SortedSet
510     */
511    public SortedSet<B> getSetB()
512    {
513        return keys.getSetB();
514    }
515
516    /**
517     * Returns a separate (shallow) copy of the set of A keys as an {@link OrderedSet}.
518     * To be called sparingly, since this allocates a new OrderedSet instead of reusing one. This can be useful if you
519     * were going to copy the set produced by {@link #getSetA()} anyway.
520     * @return the A keys as an OrderedSet
521     */
522    public OrderedSet<A> getOrderedSetA() {
523        return keys.getOrderedSetA();
524    }
525    
526    /**
527     * Returns a separate (shallow) copy of the set of B keys as an {@link OrderedSet}.
528     * To be called sparingly, since this allocates a new OrderedSet instead of reusing one. This can be useful if you
529     * were going to copy the set produced by {@link #getSetB()} anyway.
530     * @return the B keys as an OrderedSet
531     */
532    public OrderedSet<B> getOrderedSetB() {
533        return keys.getOrderedSetB();
534    }
535
536    /**
537     * Gets the Q values as a shared reference to the ArrayList of Q this uses; like {@link #getSetA()} and
538     * {@link #getSetB()}, changes made to the returned list will also change this data structure.
539     * It retains the current ordering.
540     * @return the Q values as an ArrayList, shared with this K2V1
541     */
542    public ArrayList<Q> getListQ()
543    {
544        return values;
545    }
546
547    /**
548     * Gets the Q values as a freshly-copied ArrayList of Q; unlike {@link #getSetA()} or {@link #getSetB()}, this does
549     * not cache the value list. It retains the current ordering.
550     * @return the Q values as an ArrayList, copied from this K2V1's internal list
551     */
552    public ArrayList<Q> getArrayListQ()
553    {
554        return new ArrayList<>(values);
555    }
556
557    /**
558     * Gets the size of this collection. That's the number of A keys, which is always the same as the number of B keys,
559     * which is always the same as the number of indices, which is also always the same as the size of the values List.
560     * @return the current number of indices in this K2V1, which can be thought of as the number of A keys
561     */
562    public int size()
563    {
564        return keys.keysA.size;
565    }
566
567    /**
568     * I think you can guess what this does.
569     * @return true if there are no items in this K2V1; false if there are items present
570     */
571    public boolean isEmpty()
572    {
573        return values.isEmpty();
574    }
575
576    public int keyCount() {
577        return 2;
578    }
579    public int valueCount() {
580        return 1;
581    }
582
583}