001package squidpony.squidmath;
002
003import squidpony.ArrayTools;
004
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.Iterator;
008import java.util.SortedSet;
009
010/**
011 * An ordered bidirectional map-like data structure, with "bundles" of unique E1 (Element type 1) keys and unique E2
012 * (Element type 2) keys updated together like a map that can be queried by E1 key bundles, E2 key bundles, or int
013 * indices, as well as like a multimap that can be queried by lone E1 or E2 keys. A bundle can be specified as a
014 * Collection of E1 or E2 values, an array of E1 or E2 values, or in two parts as either of the above given to a
015 * BundleBiMap method along with an int array that can represent variations on keys (e.g. some quantity for an E1 or E2
016 * value that permits different amounts to be passed with the unique element values to it yield a different E2 bundle
017 * for, say, 1 Foo and 1 Bar vs. 2 Foo and 3 Bar). This allows an intended purpose for this class as a way to describe
018 * crafting recipes that may need various amounts of an item to be mixed in and for multiple items with their own
019 * quantities to be produced, and since the quantities are optional, it can still be used to group multiple E1 keys with
020 * multiple E2 keys. The multimap property allows you to use an E1 or E2 element key to get all of the E2 or E1 keys
021 * that are associated with a bundle that contains that E1 or E2 key.
022 * <br>
023 * Does not implement any interfaces that you would expect for a data structure, because almost every method it has
024 * needs to specify whether it applies to E1 or E2 items, or otherwise doesn't fit a normal Collection-like interface's
025 * requirements.
026 * <br>
027 * Closely related to BundleBiMap, Arrangement, K2, and K2V1 in implementation.
028 * <br>
029 * Created by Tommy Ettinger on 4/26/2017.
030 */
031public class BundleBundleBiMap<E1, E2>
032{
033    private Arrangement<E1> elements1;
034    private Arrangement<E2> elements2;
035    private K2<int[][], int[][]> bm;
036    private ArrayList<IntVLA> mm1, mm2;
037
038    /**
039     * Constructs an empty BundleBiMap.
040     */
041    public BundleBundleBiMap()
042    {
043        this(Arrangement.DEFAULT_INITIAL_SIZE, Arrangement.DEFAULT_LOAD_FACTOR);
044    }
045
046    /**
047     * Constructs a BundleBiMap with the expected number of indices to hold (the number of bundles and number of E2 items
048     * is always the same, and this will be more efficient if expected is greater than that number).
049     * @param expected how many bundle-to-single pairings this is expected to hold
050     */
051    public BundleBundleBiMap(int expected)
052    {
053        this(expected, Arrangement.DEFAULT_LOAD_FACTOR);
054    }
055
056    /**
057     * Constructs a BundleBiMap with the expected number of indices to hold (the number of bundles and number of E2 items
058     * is always the same, and this will be more efficient if expected is greater than that number) and the load factor
059     * to use, between 0.1f and 0.8f usually (using load factors higher than 0.8f can cause problems).
060     * @param expected the amount of indices (the count of bundles is the same as the count of E2 items) this should hold
061     * @param f the load factor, probably between 0.1f and 0.8f
062     */
063    public BundleBundleBiMap(int expected, float f)
064    {
065        elements1 = new Arrangement<>(expected, f);
066        elements2 = new Arrangement<>(expected, f);
067        bm = new K2<>(expected, f, CrossHash.int2DHasher, CrossHash.int2DHasher);
068        mm1 = new ArrayList<>(expected * 4);
069        mm2 = new ArrayList<>(expected * 4);
070    }
071
072
073    /**
074     * Constructs a BundleBiMap using another BundleBiMap to copy.
075     * @param other the other BundleBiMap to copy
076     */
077    public BundleBundleBiMap(BundleBundleBiMap<E1, E2> other)
078    {
079        if(other == null)
080        {
081            elements1 = new Arrangement<>(64, 0.75f);
082            elements2 = new Arrangement<>(64, 0.75f);
083            bm = new K2<>(16, 0.75f, CrossHash.int2DHasher, CrossHash.int2DHasher);
084            mm1 = new ArrayList<>(64);
085            mm2 = new ArrayList<>(64);
086        }
087        else
088        {
089            elements1 = new Arrangement<>(other.elements1);
090            elements2 = new Arrangement<>(other.elements2);
091            bm = new K2<>(other.bm);
092            mm1 = new ArrayList<>(other.mm1);
093            mm2 = new ArrayList<>(other.mm2);
094        }
095    }
096
097    /**
098     * Returns true if this contains the E1, element, in any of its bundles of E1 keys.
099     * @param element the E1 to check the presence of in all bundles
100     * @return true if element is present in this; false otherwise
101     */
102    public boolean containsElement1(E1 element) { return elements1.containsKey(element); }
103    /**
104     * Returns true if this contains the E2, element, in its collection of E2 items.
105     * @param element the E2 to check the presence of
106     * @return true if element is present in this; false otherwise
107     */
108    public boolean containsElement2(E2 element) { return elements2.containsKey(element); }
109
110    /**
111     * Returns true if index is between 0 (inclusive) and {@link #size()} (exclusive), or false otherwise.
112     * @param index the index to check
113     * @return true if index is a valid index in the ordering of this BundleBiMap
114     */
115    public boolean containsIndex(int index) { return index >= 0 && index < bm.size(); }
116
117    /**
118     * Gets a random E1 from this BundleBiMap's individual elements1 using the given IRNG.
119     * @param random generates a random index to get an E1 with
120     * @return a randomly chosen E1, or null if this is empty
121     */
122    public E1 randomElement1(IRNG random)
123    {
124        return elements1.randomKey(random);
125    }
126
127    /**
128     * Gets a random E2 from this BundleBiMap using the given IRNG.
129     * @param random generates a random index to get a E2 with
130     * @return a randomly chosen E2, or null if this is empty
131     */
132    public E2 randomElement2(IRNG random)
133    {
134        return elements2.randomKey(random);
135    }
136
137    /**
138     * Adds a bundle of E1 keys and a E2 key at the same point in the ordering (the end) to this BundleBiMap. Neither
139     * parameter can be present in this collection before this is called.
140     * @param e1 an array of E1 keys to add; the array cannot already be present, nor can it be null
141     * @param e2 an array of E2 keys to add; the array cannot already be present, nor can it be null
142     * @return true if this collection changed as e1 result of this call
143     */
144    public boolean put(E1[] e1, E2[] e2)
145    {
146        if(e1 == null || e2 == null) return false;
147        int len = elements1.size;
148        elements1.putAll(e1);
149        for (int i = len; i < elements1.size; i++) {
150            mm1.add(new IntVLA(4));
151        }
152        len = elements2.size;
153        elements2.putAll(e2);
154        for (int i = len; i < elements2.size; i++) {
155            mm2.add(new IntVLA(4));
156        }
157        int[][] bundle1 = new int[][]{elements1.getArray(e1)};
158        int[][] bundle2 = new int[][]{elements2.getArray(e2)};
159        if(!bm.put(bundle1, bundle2))
160            return false;
161        len = e1.length;
162        for (int i = 0; i < len; i++) {
163            mm1.get(bundle1[0][i]).add(bm.size()-1);
164        }
165        len = e2.length;
166        for (int i = 0; i < len; i++) {
167            mm2.get(bundle2[0][i]).add(bm.size()-1);
168        }
169        return true;
170    }
171    /**
172     * Adds a bundle of E1 keys, mixed with an int array of variations, and a E2 key at the same point in the ordering
173     * (the end) to this BundleBiMap. Neither the E2 key nor the bundle (effectively, the pair of e1 and variation) can be
174     * present in this collection before this is called.
175     * @param e1 an array of E1 keys to add; the array cannot already have been inserted with an equivalent variation, nor can it be null
176     * @param variation1 an int array that can be used to make a different version of e1, i.e. the same things at different quantities; cannot be null
177     * @param e2 an array of E2 keys to add; the array cannot already have been inserted with an equivalent variation, nor can it be null
178     * @param variation2 an int array that can be used to make a different version of e2, i.e. the same things at different quantities; cannot be null
179     * @return true if this collection changed as e1 result of this call
180     */
181    public boolean put(E1[] e1, int[] variation1, E2[] e2, int[] variation2)
182    {
183        if(e1 == null || e2 == null) return false;
184        int len = elements1.size;
185        elements1.putAll(e1);
186        for (int i = len; i < elements1.size; i++) {
187            mm1.add(new IntVLA(4));
188        }
189        len = elements2.size;
190        elements2.putAll(e2);
191        for (int i = len; i < elements2.size; i++) {
192            mm2.add(new IntVLA(4));
193        }
194        int[][] bundle1, bundle2;
195        if(variation1 == null)
196            bundle1 = new int[][]{elements1.getArray(e1)};
197        else
198            bundle1 = new int[][]{elements1.getArray(e1), variation1};
199        if(variation2 == null)
200            bundle2 = new int[][]{elements2.getArray(e2)};
201        else
202            bundle2 = new int[][]{elements2.getArray(e2), variation2};
203        if(!bm.put(bundle1, bundle2))
204            return false;
205        len = e1.length;
206        for (int i = 0; i < len; i++) {
207            mm1.get(bundle1[0][i]).add(bm.size()-1);
208        }
209        len = e2.length;
210        for (int i = 0; i < len; i++) {
211            mm2.get(bundle2[0][i]).add(bm.size()-1);
212        }
213        return true;
214    }
215    /**
216     * Adds a bundle of E1 keys and a E2 key at the same point in the ordering (the end) to this BundleBiMap. Neither
217     * parameter can be present in this collection before this is called.
218     * @param e1 a Collection of E1 keys to add; the contents cannot already be present, nor can it be null
219     * @param e2 a Collection of E2 keys to add; the contents cannot already be present, nor can it be null
220     * @return true if this collection changed as a result of this call
221     */
222    public boolean put(Collection<? extends E1> e1, Collection<? extends E2> e2)
223    {
224        if(e1 == null || e2 == null) return false;
225        int len = elements1.size;
226        elements1.putAll(e1);
227        for (int i = len; i < elements1.size; i++) {
228            mm1.add(new IntVLA(4));
229        }
230        len = elements2.size;
231        elements2.putAll(e2);
232        for (int i = len; i < elements2.size; i++) {
233            mm2.add(new IntVLA(4));
234        }
235        int[][] bundle1 = new int[][]{elements1.getArray(e1)};
236        int[][] bundle2 = new int[][]{elements2.getArray(e2)};
237        if(!bm.put(bundle1, bundle2))
238            return false;
239        len = e1.size();
240        for (int i = 0; i < len; i++) {
241            mm1.get(bundle1[0][i]).add(bm.size()-1);
242        }
243        len = e2.size();
244        for (int i = 0; i < len; i++) {
245            mm2.get(bundle2[0][i]).add(bm.size()-1);
246        }
247        return true;
248
249    }
250    /**
251     * Adds a bundle of E1 keys, mixed with an int array of variations, and a bundle of E2 keys at the same point in the
252     * ordering (the end) to this BundleBiMap. Neither the E1 bundle nor E2 bundle (effectively, the pair of element
253     * collection and variation) can be present in this collection before this is called.
254     * @param e1 a Collection of E1 keys to add; the Collection cannot already have been inserted with an equivalent variation, nor can it be null
255     * @param variation1 an int array that can be used to make a different version of e1, i.e. the same things at different quantities; cannot be null
256     * @param e2 a Collection of E2 keys to add; the Collection cannot already have been inserted with an equivalent variation, nor can it be null
257     * @param variation2 an int array that can be used to make a different version of e2, i.e. the same things at different quantities; cannot be null
258     * @return true if this collection changed as a result of this call
259     */
260    public boolean put(Collection<? extends E1> e1, int[] variation1, Collection<? extends E2> e2, int[] variation2)
261    {
262        if(e1 == null || e2 == null) return false;
263        int len = elements1.size;
264        elements1.putAll(e1);
265        for (int i = len; i < elements1.size; i++) {
266            mm1.add(new IntVLA(4));
267        }
268        len = elements2.size;
269        elements2.putAll(e2);
270        for (int i = len; i < elements2.size; i++) {
271            mm2.add(new IntVLA(4));
272        }
273        int[][] bundle1, bundle2;
274        if(variation1 == null)
275            bundle1 = new int[][]{elements1.getArray(e1)};
276        else
277            bundle1 = new int[][]{elements1.getArray(e1), variation1};
278        if(variation2 == null)
279            bundle2 = new int[][]{elements2.getArray(e2)};
280        else
281            bundle2 = new int[][]{elements2.getArray(e2), variation2};
282        if(!bm.put(bundle1, bundle2))
283            return false;
284        len = e1.size();
285        for (int i = 0; i < len; i++) {
286            mm1.get(bundle1[0][i]).add(bm.size()-1);
287        }
288        len = e2.size();
289        for (int i = 0; i < len; i++) {
290            mm2.get(bundle2[0][i]).add(bm.size()-1);
291        }
292        return true;
293    }
294
295    /**
296     * Puts all unique E1 and E2 keys in {@code aKeys} and {@code bKeys} into this K2 at the end. If an E1 in aKeys or a E2
297     * in bKeys is already present when this would add one, this will not put the E1 and E2 keys at that point in the
298     * iteration order, and will place the next unique E1 and E2 it finds in the arguments at that position instead.
299     * @param aKeys an Iterable or Collection of E1 keys to add; should all be unique (like a Set)
300     * @param bKeys an Iterable or Collection of E2 keys to add; should all be unique (like a Set)
301     * @return true if this collection changed as a result of this call
302     */
303    public boolean putAll(Iterable<? extends Collection<? extends E1>> aKeys, Iterable<? extends Collection<? extends E2>> bKeys)
304    {
305        if(aKeys == null || bKeys == null) return false;
306        Iterator<? extends Collection<? extends E1>> aIt = aKeys.iterator();
307        Iterator<? extends Collection<? extends E2>> bIt = bKeys.iterator();
308        boolean changed = false;
309        while (aIt.hasNext() && bIt.hasNext())
310        {
311            changed = put(aIt.next(), bIt.next()) || changed;
312        }
313        return changed;
314    }
315    /**
316     * Puts all unique E1 and E2 keys in {@code other} into this K2, respecting other's ordering. If an E1 or a E2 in other
317     * is already present when this would add one, this will not put the E1 and E2 keys at that point in the iteration
318     * order, and will place the next unique E1 and E2 it finds in the arguments at that position instead.
319     * @param other another K2 collection with the same E1 and E2 types
320     * @return true if this collection changed as a result of this call
321     */
322    public boolean putAll(BundleBundleBiMap<? extends E1, ? extends E2> other)
323    {
324        if(other == null) return false;
325        boolean changed = false;
326        int sz = other.size();
327        for (int i = 0; i < sz; i++) {
328            int[][] bundle1 = other.bm.getAAt(i), bundle2 = other.bm.getBAt(i);
329            if(bundle1 == null || bundle1.length == 0 || bundle2 == null || bundle2.length == 0) continue;
330            if(bundle1.length == 1 && bundle2.length == 1)
331            {
332                changed |= put(elements1.keysAt(bundle1[0]), elements2.keysAt(bundle2[0]));
333            }
334            else
335            {
336                changed |= put(elements1.keysAt(bundle1[0]), bundle1.length > 1 ? bundle1[1] : null,
337                        elements2.keysAt(bundle2[0]), bundle2.length > 1 ? bundle2[1] : null);
338            }
339        }
340        return changed;
341    }
342
343    /**
344     * Given an index to look up, gets a 2D int array representing the bundle at that index. The bundle is likely to
345     * use a representation for the first sub-array that will be meaningless without internal information from this
346     * BundleBiMap, but the second sub-array, if present, should match the variation supplied with that bundle
347     * to {@link #put(Object[], int[], Object[], int[])} or {@link #put(Collection, int[], Collection, int[])}.
348     * <br>
349     * This method copies the 2D int array it returns, so modifying it won't affect the original BundleBiMap.
350     * @param index an int index to look up
351     * @return a 2D int array that represents a bundle, or null if index is out of bounds
352     */
353    public int[][] getCodeAt(int index)
354    {
355        return ArrayTools.copy(bm.getAAt(index));
356    }
357    /**
358     * Given an index to look up, gets the E2 key present at that position in the ordering.
359     * @param index an int index to look up
360     */
361    public OrderedSet<E2> getElement2At(int index)
362    {
363        return elements2.keysAt(bm.getBAt(index)[0]);
364    }
365
366    /**
367     * Given a bundle of E1 keys as an array with no variation, gets the matching E2 key for that bundle, or null if there
368     * is none.
369     * @param e1 an array of E1
370     * @return the E2 keys that correspond to the given bundle, e1 combined with variations
371     */
372    public OrderedSet<E2> getElement2(E1[] e1)
373    {
374        if(e1 == null) return null;
375        return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1)})[0]);
376    }
377
378
379    /**
380     * Given a bundle of E1 keys as an array with an int array variation, gets the matching E2 key for that bundle, or
381     * null if there is none.
382     * @param e1 an array of E1 element keys
383     * @param variations an int array that should match an int array used as a variation parameter to put
384     * @return the E2 keys that correspond to the given bundle, e1 combined with variations
385     */
386    public OrderedSet<E2> getElement2(E1[] e1, int[] variations)
387    {
388        if(e1 == null || variations == null) return null;
389        return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1), variations})[0]);
390    }
391
392    /**
393     * Given a bundle of E1 keys as a Collection with no variation, gets the matching E2 key for that bundle, or
394     * null if there is none.
395     * @param e1 a Collection of E1 element keys (each key can be an E1 or an instance of any subclass of E1)
396     * @return the E2 key that corresponds to the given bundle, e
397     */
398    public OrderedSet<E2> getElement2(Collection<? extends E1> e1)
399    {
400        if(e1 == null) return null;
401        return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1)})[0]);
402    }
403
404    /**
405     * Given a bundle of E1 keys as a Collection with an int array variation, gets the matching E2 key for that bundle, or
406     * null if there is none.
407     * @param e1 a Collection of E1 element keys (each key can be an E1 or an instance of any subclass of E1)
408     * @param variations an int array that should match an int array used as a variation parameter to put
409     * @return the E2 key that corresponds to the given bundle, e1 combined with variations
410     */
411    public OrderedSet<E2> getElement2(Collection<? extends E1> e1, int[] variations)
412    {
413        if(e1 == null || variations == null) return null;
414        return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1), variations})[0]);
415    }
416    /**
417     * Given an E1 bundle as a coded 2D int array, gets the matching E2 bundle, also coded, or null if there is none.
418     * The code parameter should be obtained from one of the methods that specifically returns that kind of 2D array,
419     * since the code uses internal information to efficiently represent a bundle.
420     * @param code a 2D int array representing a bundle that should have been obtained directly from this object
421     * @return the E2 key that corresponds to the given coded bundle
422     */
423    public int[][] getElement2Coded(int[][] code)
424    {
425        if(code == null) return null;
426        return bm.getBFromA(code);
427    }
428
429    /**
430     * Gets (in near-constant time) the index of the given E2 coded key in the ordering.
431     * @param code a E2 coded key to look up
432     * @return the position in the ordering of code
433     */
434    public int indexOfElement2Coded(int[][] code)
435    {
436        return bm.indexOfB(code);
437    }
438
439    /**
440     * Given a coded bundle as produced by some methods in this class, decodes the elements1 part of the bundle and
441     * returns it as a newly-allocated OrderedSet of E1 element keys.
442     * @param bundle a coded bundle as a 2D int array
443     * @return an OrderedSet of E1 element keys corresponding to the data coded into bundle
444     */
445    public OrderedSet<E1> elementsFromCode(int[][] bundle)
446    {
447        if(bundle == null || bundle.length < 1 || bundle[0] == null) return null;
448        return elements1.keysAt(bundle[0]);
449    }
450
451    /**
452     * Given a coded bundle as produced by some methods in this class, decodes the variation part of the bundle, if
453     * present, and returns it as a newly-allocated 1D int array. If there is no variation in the given coded bundle,
454     * this returns null.
455     * @param bundle a coded bundle as a 2D int array
456     * @return the variation part of the data coded into bundle
457     */
458    public int[] variationFromCode(int[][] bundle)
459    {
460        if(bundle == null || bundle.length < 2 || bundle[1] == null) return null;
461        int[] ret = new int[bundle[1].length];
462        System.arraycopy(bundle[1], 0, ret, 0, ret.length);
463        return ret;
464    }
465
466    /**
467     * Given an E2 key to look up, gets a (newly-allocated) OrderedSet of E1 element keys corresponding to that E2 key.
468     * If a variation is part of the bundle, it will not be present in this, but a copy can be retrieved with
469     * {@link #getElement1BundleVariation(Object[])}.
470     * @param lookup a E2 key to look up
471     * @return an OrderedSet of the E1 elements1 used in the bundle that corresponds to single, or null if invalid
472     */
473    public OrderedSet<E1> getBundleElements(E2[] lookup)
474    {
475        int[][] bundle;
476        if(lookup == null || (bundle = bm.getAFromB(new int[][]{elements2.getArray(lookup)})) == null
477                || bundle.length < 1 || bundle[0] == null) return null;
478        return elements1.keysAt(bundle[0]);
479    }
480
481    /**
482     * Given an E2 key to look up, gets a (newly-allocated) int array that is equivalent to the variation part of the
483     * bundle corresponding to single. If there is no variation in the corresponding bundle, then this returns null.
484     * To get the E1 elements1 that are the main part of a bundle, you can use {@link #getBundleElements(Object[])}.
485     * @param lookup a bundle of E2 keys to look up
486     * @return the variation part of the E1 bundle that corresponds to the looked-up bundle, or null if invalid
487     */
488    public int[] getElement1BundleVariation(E2[] lookup)
489    {
490        int[][] bundle;
491        if(lookup == null || (bundle = bm.getAFromB(new int[][]{elements2.getArray(lookup)})) == null
492                || bundle.length < 2 || bundle[1] == null) return null;
493        int[] ret = new int[bundle[1].length];
494        System.arraycopy(bundle[1], 0, ret, 0, ret.length);
495        return ret;
496    }
497
498    /**
499     * Given an index to look up, gets a (newly-allocated) OrderedSet of E1 element keys in the bundle at that index.
500     * If a variation is part of the bundle, it will not be present in this, but a copy can be retrieved with
501     * {@link #getBundleVariationAt(int)}.
502     * @param index an int position in the ordering to look up
503     * @return an OrderedSet of the E1 elements1 used in the bundle present at index, or null if invalid
504     */
505    public OrderedSet<E1> getBundleElementsAt(int index)
506    {
507        int[][] bundle;
508        if((bundle = bm.getAAt(index)) == null || bundle.length < 1) return null;
509        return elements1.keysAt(bundle[0]);
510    }
511    /**
512     * Given an index to look up, gets a (newly-allocated) int array that is equivalent to the variation part of the
513     * bundle present at that index. If there is no variation in the corresponding bundle, then this returns null.
514     * To get the E1 elements1 that are the main part of a bundle, you can use {@link #getBundleElementsAt(int)}.
515     * @param index an int position in the ordering to look up
516     * @return an int array copied from the variation part of the bundle present at index, or null if invalid
517     */
518    public int[] getBundleVariationAt(int index)
519    {
520        int[][] bundle;
521        if((bundle = bm.getAAt(index)) == null || bundle.length < 2 || bundle[1] == null) return null;
522        int[] ret = new int[bundle[1].length];
523        System.arraycopy(bundle[1], 0, ret, 0, ret.length);
524        return ret;
525    }
526
527    /**
528     * Given an E1 element key that could be used in one or more bundles this uses, finds all E2 single keys corresponding
529     * to bundles that contain the given element. Thus, if E1 was String and this BundleBiMap contained ["Copper", "Tin"]
530     * mapped to "Bronze" and ["Zinc", "Copper"] mapped to "Brass", then calling this method with "Copper" would get an
531     * OrderedSet that contains ["Bronze", "Brass"].
532     * @param element an E1 element key to look up (probably a component of one or more bundles)
533     * @return an OrderedSet of all E2 keys where the given element is part of the bundle corresponding to that E2 key, or null if E1 does not match anything
534     */
535    public OrderedSet<OrderedSet<? extends E2>> getManyElement2(E1 element)
536    {
537        if(element == null) return null;
538        int pos;
539        if((pos = elements1.getInt(element)) < 0) return null;
540
541        IntVLA positions = mm1.get(pos);
542        OrderedSet<OrderedSet<? extends E2>> ks = new OrderedSet<>(positions.size);
543        for(int i = 0; i < positions.size; i++)
544        {
545            ks.add(getElement2At(positions.get(i)));
546        }
547        return ks;
548    }
549    /**
550     * Given an E1 element key that could be used in one or more bundles this uses, gets all bundles in this object that
551     * contain the given element, as coded 2D int arrays. These coded bundles can be given to
552     * {@link #elementsFromCode(int[][])} and {@link #variationFromCode(int[][])} to get usable information from them.
553     * @param element an E1 element key to look up (probably a component of one or more bundles)
554     * @return an OrderedSet of all coded 2D int array bundles that contain the given element, or null if E1 is not in any bundles
555     */
556    public OrderedSet<int[][]> getManyCoded(E1 element)
557    {
558        if(element == null) return null;
559        int pos;
560        if((pos = elements1.getInt(element)) < 0) return null;
561        return bm.keysA.keysAt(mm1.get(pos));
562    }
563
564    /**
565     * Given an E1 element key that could be used in one or more bundles this uses, gets all indices in the ordering
566     * that contain a bundle with that element.  From such an index, you can use {@link #getElement2At(int)} (int)} to get the
567     * E2 key at that position, {@link #getBundleElementsAt(int)} to get the E1 element keys at that position,
568     * {@link #getBundleVariationAt(int)} to get the possible variation at that position, or {@link #getCodeAt(int)} to
569     * get the coded bundle at that position.
570     * @return an OrderedSet of all coded 2D int array bundles that contain the given element, or null if E1 is not in any bundles
571     */
572    public int[] getManyIndices(E1 element)
573    {
574        if(element == null) return null;
575        int pos;
576        if((pos = elements1.getInt(element)) < 0) return null;
577        return mm1.get(pos).toArray();
578    }
579
580    /**
581     * Reorders this BundleBiMap using {@code ordering}, which has the same length as this object's {@link #size()}
582     * and can be generated with {@link ArrayTools#range(int)} (which, if applied, would produce no
583     * change to the current ordering), {@link IRNG#randomOrdering(int)} (which gives a random ordering, and if
584     * applied immediately would be the same as calling {@link #shuffle(IRNG)}), or made in some other way. If you
585     * already have an ordering and want to make a different ordering that can undo the change, you can use
586     * {@link ArrayTools#invertOrdering(int[])} called on the original ordering. The effects of this method, if called
587     * with an ordering that has repeat occurrences of an int or contains ints that are larger than its size should
588     * permit, are undefined other than the vague definition of "probably bad, unless you like crashes."
589     * @param ordering an int array or vararg that must contain each int from 0 to {@link #size()}
590     * @return this for chaining
591     */
592    public BundleBundleBiMap<E1, E2> reorder(int... ordering)
593    {
594        if(ordering == null || ordering.length != bm.size()) return this;
595        bm.reorder(ordering);
596        int len = mm1.size();
597        for (int i = 0; i < len; i++) {
598            IntVLA iv = mm1.get(i);
599            for (int j = 0; j < iv.size; j++) {
600                iv.set(i, ordering[iv.get(i)]);
601            }
602        }
603        return this;
604    }
605
606    /**
607     * Generates a random ordering with rng and applies the same ordering to all kinds of keys this has; they will
608     * maintain their current association to other keys but their ordering/indices will change.
609     * @param rng an IRNG to produce the random ordering this will use
610     * @return this for chaining
611     */
612    public BundleBundleBiMap<E1, E2> shuffle(IRNG rng)
613    {
614        return reorder(rng.randomOrdering(bm.size()));
615    }
616
617    /**
618     * Creates a new iterator over the individual E1 element keys this holds, with a larger total count the iterator may
619     * yield than {@link #size()} in most cases (it should be equal to {@link #elementSize()}), and in no particular
620     * order (though the order should be stable across versions and platforms, no special means are provided to further
621     * control the order). The E1 keys are individual, not bundled, and duplicate keys should never be encountered even
622     * if an E1 key appears in many bundles.
623     * @return a newly-created iterator over this BundleBiMap's individual (non-bundled) E1 keys
624     */
625    public Iterator<E1> iteratorElement1()
626    {
627        return elements1.iterator();
628    }
629    /**
630     * Creates a new iterator over the E2 single keys this holds. The total number of items this yields should be equal
631     * to {@link #size()}. This method can be problematic for garbage collection if called very frequently; it may be
632     * better to access items by index (which also lets you access other keys associated with that index) using
633     * {@link #getElement2At(int)} in a for(int i=0...) loop.
634     * @return a newly-created iterator over this BundleBiMap's E2 keys
635     */
636    public Iterator<E2> iteratorElement2()
637    {
638        return elements2.iterator();
639    }
640
641    /**
642     * Gets and caches the individual E1 keys as a Collection that implements SortedSet (and so also implements Set).
643     * @return the E1 keys as a SortedSet
644     */
645    public SortedSet<E1> getElement1Set() {
646        return elements1.keySet();
647    }
648
649    /**
650     * Gets and caches the E2 single keys as a Collection that implements SortedSet (and so also implements Set).
651     * @return the E2 keys as a SortedSet
652     */
653    public SortedSet<E2> getElement2Set() {
654        return elements2.keySet();
655    }
656
657    /**
658     * To be called sparingly, since this allocates a new OrderedSet instead of reusing one.
659     * @return the E1 keys as an OrderedSet
660     */
661    public OrderedSet<E1> getElement1OrderedSet() {
662        return elements1.keysAsOrderedSet();
663    }
664
665    /**
666     * To be called sparingly, since this allocates a new OrderedSet instead of reusing one.
667     * @return the E2 keys as an OrderedSet
668     */
669    public OrderedSet<E2> getElement2OrderedSet() {
670        return elements2.keysAsOrderedSet();
671    }
672
673    /**
674     * Gets the total number of bundle-to-single pairs in this BundleBiMap.
675     * @return the total number of bundle keys (equivalently, the number of single keys) in this object
676     */
677    public int size() {
678        return bm.size();
679    }
680
681    /**
682     * Gets the total number of unique E1 element keys across all bundles in this BundleBiMap. Usually not the same as
683     * {@link #size()}, and ordinarily a fair amount larger, though this can also be smaller.
684     * @return the total number of unique E1 element keys in all bundles this contains
685     */
686    public int elementSize() {
687        return elements1.size();
688    }
689
690    public boolean isEmpty()
691    {
692        return bm.isEmpty();
693    }
694
695}