package squidpony.squidmath;
import squidpony.ArrayTools;
import squidpony.annotation.Beta;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.SortedSet;
/**
* An ordered bidirectional map-like data structure, with "bundles" of unique E1 (Element type 1) keys and unique E2
* (Element type 2) keys updated together like a map that can be queried by E1 key bundles, E2 key bundles, or int
* indices, as well as like a multimap that can be queried by lone E1 or E2 keys. A bundle can be specified as a
* 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
* BundleBiMap method along with an int array that can represent variations on keys (e.g. some quantity for an E1 or E2
* value that permits different amounts to be passed with the unique element values to it yield a different E2 bundle
* 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
* crafting recipes that may need various amounts of an item to be mixed in and for multiple items with their own
* quantities to be produced, and since the quantities are optional, it can still be used to group multiple E1 keys with
* 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
* that are associated with a bundle that contains that E1 or E2 key.
* <br>
* Does not implement any interfaces that you would expect for a data structure, because almost every method it has
* needs to specify whether it applies to E1 or E2 items, or otherwise doesn't fit a normal Collection-like interface's
* requirements.
* <br>
* Closely related to BundleBiMap, Arrangement, K2, and K2V1 in implementation.
* <br>
* Created by Tommy Ettinger on 4/26/2017.
*/
@Beta
public class BundleBundleBiMap<E1, E2>
{
private Arrangement<E1> elements1;
private Arrangement<E2> elements2;
private K2<int[][], int[][]> bm;
private ArrayList<IntVLA> mm1, mm2;
/**
* Constructs an empty BundleBiMap.
*/
public BundleBundleBiMap()
{
this(Arrangement.DEFAULT_INITIAL_SIZE, Arrangement.DEFAULT_LOAD_FACTOR);
}
/**
* Constructs a BundleBiMap with the expected number of indices to hold (the number of bundles and number of E2 items
* is always the same, and this will be more efficient if expected is greater than that number).
* @param expected how many bundle-to-single pairings this is expected to hold
*/
public BundleBundleBiMap(int expected)
{
this(expected, Arrangement.DEFAULT_LOAD_FACTOR);
}
/**
* Constructs a BundleBiMap with the expected number of indices to hold (the number of bundles and number of E2 items
* is always the same, and this will be more efficient if expected is greater than that number) and the load factor
* to use, between 0.1f and 0.8f usually (using load factors higher than 0.8f can cause problems).
* @param expected the amount of indices (the count of bundles is the same as the count of E2 items) this should hold
* @param f the load factor, probably between 0.1f and 0.8f
*/
public BundleBundleBiMap(int expected, float f)
{
elements1 = new Arrangement<>(expected, f);
elements2 = new Arrangement<>(expected, f);
bm = new K2<>(expected, f, CrossHash.int2DHasher, CrossHash.int2DHasher);
mm1 = new ArrayList<>(expected * 4);
mm2 = new ArrayList<>(expected * 4);
}
/**
* Constructs a BundleBiMap using another BundleBiMap to copy.
* @param other the other BundleBiMap to copy
*/
public BundleBundleBiMap(BundleBundleBiMap<? extends E1, ? extends E2> other)
{
if(other == null)
{
elements1 = new Arrangement<>(64, 0.75f);
elements2 = new Arrangement<>(64, 0.75f);
bm = new K2<>(16, 0.75f, CrossHash.int2DHasher, CrossHash.int2DHasher);
mm1 = new ArrayList<>(64);
mm2 = new ArrayList<>(64);
}
else
{
elements1 = new Arrangement<>(other.elements1);
elements2 = new Arrangement<>(other.elements2);
bm = new K2<>(other.bm);
mm1 = new ArrayList<>(other.mm1);
mm2 = new ArrayList<>(other.mm2);
}
}
/**
* Returns true if this contains the E1, element, in any of its bundles of E1 keys.
* @param element the E1 to check the presence of in all bundles
* @return true if element is present in this; false otherwise
*/
public boolean containsElement1(E1 element) { return elements1.containsKey(element); }
/**
* Returns true if this contains the E2, element, in its collection of E2 items.
* @param element the E2 to check the presence of
* @return true if element is present in this; false otherwise
*/
public boolean containsElement2(E2 element) { return elements2.containsKey(element); }
/**
* Returns true if index is between 0 (inclusive) and {@link #size()} (exclusive), or false otherwise.
* @param index the index to check
* @return true if index is a valid index in the ordering of this BundleBiMap
*/
public boolean containsIndex(int index) { return index >= 0 && index < bm.size(); }
/**
* Gets a random E1 from this BundleBiMap's individual elements1 using the given RNG.
* @param random generates a random index to get an E1 with
* @return a randomly chosen E1, or null if this is empty
*/
public E1 randomElement1(RNG random)
{
return elements1.randomKey(random);
}
/**
* Gets a random E2 from this BundleBiMap using the given RNG.
* @param random generates a random index to get a E2 with
* @return a randomly chosen E2, or null if this is empty
*/
public E2 randomElement2(RNG random)
{
return elements2.randomKey(random);
}
/**
* Adds a bundle of E1 keys and a E2 key at the same point in the ordering (the end) to this BundleBiMap. Neither
* parameter can be present in this collection before this is called.
* @param e1 an array of E1 keys to add; the array cannot already be present, nor can it be null
* @param e2 an array of E2 keys to add; the array cannot already be present, nor can it be null
* @return true if this collection changed as e1 result of this call
*/
public boolean put(E1[] e1, E2[] e2)
{
if(e1 == null || e2 == null) return false;
int len = elements1.size;
elements1.addAllIfAbsent(e1);
for (int i = len; i < elements1.size; i++) {
mm1.add(new IntVLA(4));
}
len = elements2.size;
elements2.addAllIfAbsent(e2);
for (int i = len; i < elements2.size; i++) {
mm2.add(new IntVLA(4));
}
int[][] bundle1 = new int[][]{elements1.getArray(e1)};
int[][] bundle2 = new int[][]{elements2.getArray(e2)};
if(!bm.put(bundle1, bundle2))
return false;
len = e1.length;
for (int i = 0; i < len; i++) {
mm1.get(bundle1[0][i]).add(bm.size()-1);
}
len = e2.length;
for (int i = 0; i < len; i++) {
mm2.get(bundle2[0][i]).add(bm.size()-1);
}
return true;
}
/**
* Adds a bundle of E1 keys, mixed with an int array of variations, and a E2 key at the same point in the ordering
* (the end) to this BundleBiMap. Neither the E2 key nor the bundle (effectively, the pair of e1 and variation) can be
* present in this collection before this is called.
* @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
* @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
* @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
* @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
* @return true if this collection changed as e1 result of this call
*/
public boolean put(E1[] e1, int[] variation1, E2[] e2, int[] variation2)
{
if(e1 == null || e2 == null) return false;
int len = elements1.size;
elements1.addAllIfAbsent(e1);
for (int i = len; i < elements1.size; i++) {
mm1.add(new IntVLA(4));
}
len = elements2.size;
elements2.addAllIfAbsent(e2);
for (int i = len; i < elements2.size; i++) {
mm2.add(new IntVLA(4));
}
int[][] bundle1, bundle2;
if(variation1 == null)
bundle1 = new int[][]{elements1.getArray(e1)};
else
bundle1 = new int[][]{elements1.getArray(e1), variation1};
if(variation2 == null)
bundle2 = new int[][]{elements2.getArray(e2)};
else
bundle2 = new int[][]{elements2.getArray(e2), variation2};
if(!bm.put(bundle1, bundle2))
return false;
len = e1.length;
for (int i = 0; i < len; i++) {
mm1.get(bundle1[0][i]).add(bm.size()-1);
}
len = e2.length;
for (int i = 0; i < len; i++) {
mm2.get(bundle2[0][i]).add(bm.size()-1);
}
return true;
}
/**
* Adds a bundle of E1 keys and a E2 key at the same point in the ordering (the end) to this BundleBiMap. Neither
* parameter can be present in this collection before this is called.
* @param e1 a Collection of E1 keys to add; the contents cannot already be present, nor can it be null
* @param e2 a Collection of E2 keys to add; the contents cannot already be present, nor can it be null
* @return true if this collection changed as a result of this call
*/
public boolean put(Collection<? extends E1> e1, Collection<? extends E2> e2)
{
if(e1 == null || e2 == null) return false;
int len = elements1.size;
elements1.addAllIfAbsent(e1);
for (int i = len; i < elements1.size; i++) {
mm1.add(new IntVLA(4));
}
len = elements2.size;
elements2.addAllIfAbsent(e2);
for (int i = len; i < elements2.size; i++) {
mm2.add(new IntVLA(4));
}
int[][] bundle1 = new int[][]{elements1.getArray(e1)};
int[][] bundle2 = new int[][]{elements2.getArray(e2)};
if(!bm.put(bundle1, bundle2))
return false;
len = e1.size();
for (int i = 0; i < len; i++) {
mm1.get(bundle1[0][i]).add(bm.size()-1);
}
len = e2.size();
for (int i = 0; i < len; i++) {
mm2.get(bundle2[0][i]).add(bm.size()-1);
}
return true;
}
/**
* 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
* ordering (the end) to this BundleBiMap. Neither the E1 bundle nor E2 bundle (effectively, the pair of element
* collection and variation) can be present in this collection before this is called.
* @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
* @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
* @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
* @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
* @return true if this collection changed as a result of this call
*/
public boolean put(Collection<? extends E1> e1, int[] variation1, Collection<? extends E2> e2, int[] variation2)
{
if(e1 == null || e2 == null) return false;
int len = elements1.size;
elements1.addAllIfAbsent(e1);
for (int i = len; i < elements1.size; i++) {
mm1.add(new IntVLA(4));
}
len = elements2.size;
elements2.addAllIfAbsent(e2);
for (int i = len; i < elements2.size; i++) {
mm2.add(new IntVLA(4));
}
int[][] bundle1, bundle2;
if(variation1 == null)
bundle1 = new int[][]{elements1.getArray(e1)};
else
bundle1 = new int[][]{elements1.getArray(e1), variation1};
if(variation2 == null)
bundle2 = new int[][]{elements2.getArray(e2)};
else
bundle2 = new int[][]{elements2.getArray(e2), variation2};
if(!bm.put(bundle1, bundle2))
return false;
len = e1.size();
for (int i = 0; i < len; i++) {
mm1.get(bundle1[0][i]).add(bm.size()-1);
}
len = e2.size();
for (int i = 0; i < len; i++) {
mm2.get(bundle2[0][i]).add(bm.size()-1);
}
return true;
}
/**
* 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
* in bKeys is already present when this would add one, this will not put the E1 and E2 keys at that point in the
* iteration order, and will place the next unique E1 and E2 it finds in the arguments at that position instead.
* @param aKeys an Iterable or Collection of E1 keys to add; should all be unique (like a Set)
* @param bKeys an Iterable or Collection of E2 keys to add; should all be unique (like a Set)
* @return true if this collection changed as a result of this call
*/
public boolean putAll(Iterable<? extends Collection<? extends E1>> aKeys, Iterable<? extends Collection<? extends E2>> bKeys)
{
if(aKeys == null || bKeys == null) return false;
Iterator<? extends Collection<? extends E1>> aIt = aKeys.iterator();
Iterator<? extends Collection<? extends E2>> bIt = bKeys.iterator();
boolean changed = false;
while (aIt.hasNext() && bIt.hasNext())
{
changed = put(aIt.next(), bIt.next()) || changed;
}
return changed;
}
/**
* 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
* is already present when this would add one, this will not put the E1 and E2 keys at that point in the iteration
* order, and will place the next unique E1 and E2 it finds in the arguments at that position instead.
* @param other another K2 collection with the same E1 and E2 types
* @return true if this collection changed as a result of this call
*/
public boolean putAll(BundleBundleBiMap<? extends E1, ? extends E2> other)
{
if(other == null) return false;
boolean changed = false;
int sz = other.size();
for (int i = 0; i < sz; i++) {
int[][] bundle1 = other.bm.getAAt(i), bundle2 = other.bm.getBAt(i);
if(bundle1 == null || bundle1.length == 0 || bundle2 == null || bundle2.length == 0) continue;
if(bundle1.length == 1 && bundle2.length == 1)
{
changed |= put(elements1.keysAt(bundle1[0]), elements2.keysAt(bundle2[0]));
}
else
{
changed |= put(elements1.keysAt(bundle1[0]), bundle1.length > 1 ? bundle1[1] : null,
elements2.keysAt(bundle2[0]), bundle2.length > 1 ? bundle2[1] : null);
}
}
return changed;
}
/**
* Given an index to look up, gets a 2D int array representing the bundle at that index. The bundle is likely to
* use a representation for the first sub-array that will be meaningless without internal information from this
* BundleBiMap, but the second sub-array, if present, should match the variation supplied with that bundle
* to {@link #put(Object[], int[], Object[], int[])} or {@link #put(Collection, int[], Collection, int[])}.
* <br>
* This method copies the 2D int array it returns, so modifying it won't affect the original BundleBiMap.
* @param index an int index to look up
* @return a 2D int array that represents a bundle, or null if index is out of bounds
*/
public int[][] getCodeAt(int index)
{
return ArrayTools.copy(bm.getAAt(index));
}
/**
* Given an index to look up, gets the E2 key present at that position in the ordering.
* @param index an int index to look up
*/
public OrderedSet<E2> getElement2At(int index)
{
return elements2.keysAt(bm.getBAt(index)[0]);
}
/**
* Given a bundle of E1 keys as an array with no variation, gets the matching E2 key for that bundle, or null if there
* is none.
* @param e1 an array of E1
* @return the E2 keys that correspond to the given bundle, e1 combined with variations
*/
public OrderedSet<E2> getElement2(E1[] e1)
{
if(e1 == null) return null;
return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1)})[0]);
}
/**
* Given a bundle of E1 keys as an array with an int array variation, gets the matching E2 key for that bundle, or
* null if there is none.
* @param e1 an array of E1 element keys
* @param variations an int array that should match an int array used as a variation parameter to put
* @return the E2 keys that correspond to the given bundle, e1 combined with variations
*/
public OrderedSet<E2> getElement2(E1[] e1, int[] variations)
{
if(e1 == null || variations == null) return null;
return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1), variations})[0]);
}
/**
* Given a bundle of E1 keys as a Collection with no variation, gets the matching E2 key for that bundle, or
* null if there is none.
* @param e1 a Collection of E1 element keys (each key can be an E1 or an instance of any subclass of E1)
* @return the E2 key that corresponds to the given bundle, e
*/
public OrderedSet<E2> getElement2(Collection<? extends E1> e1)
{
if(e1 == null) return null;
return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1)})[0]);
}
/**
* Given a bundle of E1 keys as a Collection with an int array variation, gets the matching E2 key for that bundle, or
* null if there is none.
* @param e1 a Collection of E1 element keys (each key can be an E1 or an instance of any subclass of E1)
* @param variations an int array that should match an int array used as a variation parameter to put
* @return the E2 key that corresponds to the given bundle, e1 combined with variations
*/
public OrderedSet<E2> getElement2(Collection<? extends E1> e1, int[] variations)
{
if(e1 == null || variations == null) return null;
return elements2.keysAt(bm.getBFromA(new int[][]{elements1.getArray(e1), variations})[0]);
}
/**
* Given an E1 bundle as a coded 2D int array, gets the matching E2 bundle, also coded, or null if there is none.
* The code parameter should be obtained from one of the methods that specifically returns that kind of 2D array,
* since the code uses internal information to efficiently represent a bundle.
* @param code a 2D int array representing a bundle that should have been obtained directly from this object
* @return the E2 key that corresponds to the given coded bundle
*/
public int[][] getElement2Coded(int[][] code)
{
if(code == null) return null;
return bm.getBFromA(code);
}
/**
* Gets (in near-constant time) the index of the given E2 coded key in the ordering.
* @param code a E2 coded key to look up
* @return the position in the ordering of code
*/
public int indexOfElement2Coded(int[][] code)
{
return bm.indexOfB(code);
}
/**
* Given a coded bundle as produced by some methods in this class, decodes the elements1 part of the bundle and
* returns it as a newly-allocated OrderedSet of E1 element keys.
* @param bundle a coded bundle as a 2D int array
* @return an OrderedSet of E1 element keys corresponding to the data coded into bundle
*/
public OrderedSet<E1> elementsFromCode(int[][] bundle)
{
if(bundle == null || bundle.length < 1 || bundle[0] == null) return null;
return elements1.keysAt(bundle[0]);
}
/**
* Given a coded bundle as produced by some methods in this class, decodes the variation part of the bundle, if
* present, and returns it as a newly-allocated 1D int array. If there is no variation in the given coded bundle,
* this returns null.
* @param bundle a coded bundle as a 2D int array
* @return the variation part of the data coded into bundle
*/
public int[] variationFromCode(int[][] bundle)
{
if(bundle == null || bundle.length < 2 || bundle[1] == null) return null;
int[] ret = new int[bundle[1].length];
System.arraycopy(bundle[1], 0, ret, 0, ret.length);
return ret;
}
/**
* Given an E2 key to look up, gets a (newly-allocated) OrderedSet of E1 element keys corresponding to that E2 key.
* If a variation is part of the bundle, it will not be present in this, but a copy can be retrieved with
* {@link #getElement1BundleVariation(Object[])}.
* @param lookup a E2 key to look up
* @return an OrderedSet of the E1 elements1 used in the bundle that corresponds to single, or null if invalid
*/
public OrderedSet<E1> getBundleElements(E2[] lookup)
{
int[][] bundle;
if(lookup == null || (bundle = bm.getAFromB(new int[][]{elements2.getArray(lookup)})) == null
|| bundle.length < 1 || bundle[0] == null) return null;
return elements1.keysAt(bundle[0]);
}
/**
* Given an E2 key to look up, gets a (newly-allocated) int array that is equivalent to the variation part of the
* bundle corresponding to single. If there is no variation in the corresponding bundle, then this returns null.
* To get the E1 elements1 that are the main part of a bundle, you can use {@link #getBundleElements(Object[])}.
* @param lookup a bundle of E2 keys to look up
* @return the variation part of the E1 bundle that corresponds to the looked-up bundle, or null if invalid
*/
public int[] getElement1BundleVariation(E2[] lookup)
{
int[][] bundle;
if(lookup == null || (bundle = bm.getAFromB(new int[][]{elements2.getArray(lookup)})) == null
|| bundle.length < 2 || bundle[1] == null) return null;
int[] ret = new int[bundle[1].length];
System.arraycopy(bundle[1], 0, ret, 0, ret.length);
return ret;
}
/**
* Given an index to look up, gets a (newly-allocated) OrderedSet of E1 element keys in the bundle at that index.
* If a variation is part of the bundle, it will not be present in this, but a copy can be retrieved with
* {@link #getBundleVariationAt(int)}.
* @param index an int position in the ordering to look up
* @return an OrderedSet of the E1 elements1 used in the bundle present at index, or null if invalid
*/
public OrderedSet<E1> getBundleElementsAt(int index)
{
int[][] bundle;
if((bundle = bm.getAAt(index)) == null || bundle.length < 1) return null;
return elements1.keysAt(bundle[0]);
}
/**
* Given an index to look up, gets a (newly-allocated) int array that is equivalent to the variation part of the
* bundle present at that index. If there is no variation in the corresponding bundle, then this returns null.
* To get the E1 elements1 that are the main part of a bundle, you can use {@link #getBundleElementsAt(int)}.
* @param index an int position in the ordering to look up
* @return an int array copied from the variation part of the bundle present at index, or null if invalid
*/
public int[] getBundleVariationAt(int index)
{
int[][] bundle;
if((bundle = bm.getAAt(index)) == null || bundle.length < 2 || bundle[1] == null) return null;
int[] ret = new int[bundle[1].length];
System.arraycopy(bundle[1], 0, ret, 0, ret.length);
return ret;
}
/**
* Given an E1 element key that could be used in one or more bundles this uses, finds all E2 single keys corresponding
* to bundles that contain the given element. Thus, if E1 was String and this BundleBiMap contained ["Copper", "Tin"]
* mapped to "Bronze" and ["Zinc", "Copper"] mapped to "Brass", then calling this method with "Copper" would get an
* OrderedSet that contains ["Bronze", "Brass"].
* @param element an E1 element key to look up (probably a component of one or more bundles)
* @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
*/
public OrderedSet<OrderedSet<? extends E2>> getManyElement2(E1 element)
{
if(element == null) return null;
int pos;
if((pos = elements1.getInt(element)) < 0) return null;
IntVLA positions = mm1.get(pos);
OrderedSet<OrderedSet<? extends E2>> ks = new OrderedSet<>(positions.size);
for(int i = 0; i < positions.size; i++)
{
ks.add(getElement2At(positions.get(i)));
}
return ks;
}
/**
* Given an E1 element key that could be used in one or more bundles this uses, gets all bundles in this object that
* contain the given element, as coded 2D int arrays. These coded bundles can be given to
* {@link #elementsFromCode(int[][])} and {@link #variationFromCode(int[][])} to get usable information from them.
* @param element an E1 element key to look up (probably a component of one or more bundles)
* @return an OrderedSet of all coded 2D int array bundles that contain the given element, or null if E1 is not in any bundles
*/
public OrderedSet<int[][]> getManyCoded(E1 element)
{
if(element == null) return null;
int pos;
if((pos = elements1.getInt(element)) < 0) return null;
return bm.keysA.keysAt(mm1.get(pos));
}
/**
* Given an E1 element key that could be used in one or more bundles this uses, gets all indices in the ordering
* that contain a bundle with that element. From such an index, you can use {@link #getElement2At(int)} (int)} to get the
* E2 key at that position, {@link #getBundleElementsAt(int)} to get the E1 element keys at that position,
* {@link #getBundleVariationAt(int)} to get the possible variation at that position, or {@link #getCodeAt(int)} to
* get the coded bundle at that position.
* @return an OrderedSet of all coded 2D int array bundles that contain the given element, or null if E1 is not in any bundles
*/
public int[] getManyIndices(E1 element)
{
if(element == null) return null;
int pos;
if((pos = elements1.getInt(element)) < 0) return null;
return mm1.get(pos).toArray();
}
/**
* Reorders this BundleBiMap using {@code ordering}, which has the same length as this object's {@link #size()}
* and can be generated with {@link ArrayTools#range(int)} (which, if applied, would produce no
* change to the current ordering), {@link RNG#randomOrdering(int)} (which gives a random ordering, and if
* applied immediately would be the same as calling {@link #shuffle(RNG)}), or made in some other way. If you
* already have an ordering and want to make a different ordering that can undo the change, you can use
* {@link ArrayTools#invertOrdering(int[])} called on the original ordering. The effects of this method, if called
* with an ordering that has repeat occurrences of an int or contains ints that are larger than its size should
* permit, are undefined other than the vague definition of "probably bad, unless you like crashes."
* @param ordering an int array or vararg that must contain each int from 0 to {@link #size()}
* @return this for chaining
*/
public BundleBundleBiMap<E1, E2> reorder(int... ordering)
{
if(ordering == null || ordering.length != bm.size()) return this;
bm.reorder(ordering);
int len = mm1.size();
for (int i = 0; i < len; i++) {
IntVLA iv = mm1.get(i);
for (int j = 0; j < iv.size; j++) {
iv.set(i, ordering[iv.get(i)]);
}
}
return this;
}
/**
* Generates a random ordering with rng and applies the same ordering to all kinds of keys this has; they will
* maintain their current association to other keys but their ordering/indices will change.
* @param rng an RNG to produce the random ordering this will use
* @return this for chaining
*/
public BundleBundleBiMap<E1, E2> shuffle(RNG rng)
{
return reorder(rng.randomOrdering(bm.size()));
}
/**
* Creates a new iterator over the individual E1 element keys this holds, with a larger total count the iterator may
* yield than {@link #size()} in most cases (it should be equal to {@link #elementSize()}), and in no particular
* order (though the order should be stable across versions and platforms, no special means are provided to further
* control the order). The E1 keys are individual, not bundled, and duplicate keys should never be encountered even
* if an E1 key appears in many bundles.
* @return a newly-created iterator over this BundleBiMap's individual (non-bundled) E1 keys
*/
public Iterator<E1> iteratorElement1()
{
return elements1.iterator();
}
/**
* Creates a new iterator over the E2 single keys this holds. The total number of items this yields should be equal
* to {@link #size()}. This method can be problematic for garbage collection if called very frequently; it may be
* better to access items by index (which also lets you access other keys associated with that index) using
* {@link #getElement2At(int)} in a for(int i=0...) loop.
* @return a newly-created iterator over this BundleBiMap's E2 keys
*/
public Iterator<E2> iteratorElement2()
{
return elements2.iterator();
}
/**
* Gets and caches the individual E1 keys as a Collection that implements SortedSet (and so also implements Set).
* @return the E1 keys as a SortedSet
*/
public SortedSet<E1> getElement1Set() {
return elements1.keySet();
}
/**
* Gets and caches the E2 single keys as a Collection that implements SortedSet (and so also implements Set).
* @return the E2 keys as a SortedSet
*/
public SortedSet<E2> getElement2Set() {
return elements2.keySet();
}
/**
* To be called sparingly, since this allocates a new OrderedSet instead of reusing one.
* @return the E1 keys as an OrderedSet
*/
public OrderedSet<E1> getElement1OrderedSet() {
return elements1.keysAsOrderedSet();
}
/**
* To be called sparingly, since this allocates a new OrderedSet instead of reusing one.
* @return the E2 keys as an OrderedSet
*/
public OrderedSet<E2> getElement2OrderedSet() {
return elements2.keysAsOrderedSet();
}
/**
* Gets the total number of bundle-to-single pairs in this BundleBiMap.
* @return the total number of bundle keys (equivalently, the number of single keys) in this object
*/
public int size() {
return bm.size();
}
/**
* Gets the total number of unique E1 element keys across all bundles in this BundleBiMap. Usually not the same as
* {@link #size()}, and ordinarily a fair amount larger, though this can also be smaller.
* @return the total number of unique E1 element keys in all bundles this contains
*/
public int elementSize() {
return elements1.size();
}
public boolean isEmpty()
{
return bm.isEmpty();
}
}