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 E (for Element) keys and unique S (for
* Single) keys updated together like a map that can be queried by E key bundles, S keys, or int indices, as well as
* like a multimap that can be queried by lone E keys. A bundle can be specified as a Collection of E values, an array
* of E 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 E keys (e.g. some quantity for an E value that permits different amounts to be passed with
* the unique E values to getS and have it yield a different S for a, 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 since the quantities are optional, it can still be used to group multiple E keys with S
* keys. The multimap property allows you to use an E element key to get all of the S keys that are associated with a
* bundle that contains that E key (you can also get the ordering indices for those S keys, which you can use to get the
* full bundle if you want).
* <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 E or S items, or otherwise doesn't fit a normal Collection-like interface's
* requirements.
* <br>
* Closely related to Arrangement, K2, and K2V1 in implementation.
* <br>
* Created by Tommy Ettinger on 4/26/2017.
*/
@Beta
public class BundleBiMap<E, S>
{
private Arrangement<E> elements;
private K2<int[][], S> bm;
private ArrayList<IntVLA> mm;
/**
* Constructs an empty BundleBiMap.
*/
public BundleBiMap()
{
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 S 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 BundleBiMap(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 S 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 S items) this should hold
* @param f the load factor, probably between 0.1f and 0.8f
*/
public BundleBiMap(int expected, float f)
{
elements = new Arrangement<>(expected, f);
bm = new K2<>(expected, f, CrossHash.int2DHasher, CrossHash.defaultHasher);
mm = new ArrayList<>(expected * 4);
}
/**
* Constructs a BundleBiMap using another BundleBiMap to copy.
* @param other the other BundleBiMap to copy
*/
public BundleBiMap(BundleBiMap<? extends E, ? extends S> other)
{
if(other == null)
{
elements = new Arrangement<>(64, 0.75f);
bm = new K2<>(16, 0.75f, CrossHash.int2DHasher, CrossHash.defaultHasher);
mm = new ArrayList<>(64);
}
else
{
elements = new Arrangement<>(other.elements);
bm = new K2<>(other.bm);
mm = new ArrayList<>(other.mm);
}
}
/**
* Returns true if this contains the E, element, in any of its bundles of E keys.
* @param element the E to check the presence of in all bundles
* @return true if element is present in this; false otherwise
*/
public boolean containsElement(E element) { return elements.containsKey(element); }
/**
* Returns true if this contains the S, single, in its collection of S items.
* @param single the S to check the presence of
* @return true if single is present in this; false otherwise
*/
public boolean containsSingle(S single) { return bm.containsB(single); }
/**
* 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(); }
/**
* Given an int index, finds the associated S key (using index as a point in the ordering).
* @param index an int index into this BundleBiMap
* @return the S object with index for its position in the ordering, or null if index was invalid
*/
public S getSingleAt(int index)
{
return bm.getBAt(index);
}
/**
* Gets a random E from this BundleBiMap's individual elements using the given RNG.
* @param random generates a random index to get an E with
* @return a randomly chosen E, or null if this is empty
*/
public E randomElement(RNG random)
{
return elements.randomKey(random);
}
/**
* Gets a random S from this BundleBiMap using the given RNG.
* @param random generates a random index to get a S with
* @return a randomly chosen S, or null if this is empty
*/
public S randomSingle(RNG random)
{
return bm.randomB(random);
}
/**
* Changes an existing S key, {@code past}, to another S key, {@code future}, if past exists in this BundleBiMap
* and future does not yet exist in this BundleBiMap. This will retain past's point in the ordering for future, so
* the associated other key(s) will still be associated in the same way.
* @param past a S key, that must exist in this BundleBiMap's S keys, and will be changed
* @param future a S key, that cannot currently exist in this BundleBiMap's S keys, but will if this succeeds
* @return this for chaining
*/
public BundleBiMap<E, S> alterB(S past, S future)
{
bm.alterB(past, future);
return this;
}
/**
* Changes the S key at {@code index} to another S key, {@code future}, if index is valid and future does not
* yet exist in this K2. The position in the ordering for future will be the same as index, and the same
* as the key this replaced, if this succeeds, so the other key(s) at that position will still be associated in
* the same way.
* @param index a position in the ordering to change; must be at least 0 and less than {@link #size()}
* @param future a S key, that cannot currently exist in this BundleBiMap's S keys, but will if this succeeds
* @return this for chaining
*/
public BundleBiMap<E, S> alterSingleAt(int index, S future)
{
bm.alterBAt(index, future);
return this;
}
/**
* Adds a bundle of E keys and a S 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 e an array of E keys to add; the array cannot already be present, nor can it be null
* @param s e S key to add; cannot already be present
* @return true if this collection changed as e result of this call
*/
public boolean put(E[] e, S s)
{
if(e == null) return false;
int len = elements.size;
elements.addAllIfAbsent(e);
for (int i = len; i < elements.size; i++) {
mm.add(new IntVLA(4));
}
elements.addAllIfAbsent(e);
int[][] bundle = new int[][]{elements.getArray(e)};
if(!bm.put(bundle, s))
return false;
len = e.length;
for (int i = 0; i < len; i++) {
mm.get(bundle[0][i]).add(bm.size()-1);
}
return true;
}
/**
* Adds a bundle of E keys, mixed with an int array of variations, and a S key at the same point in the ordering
* (the end) to this BundleBiMap. Neither the S key nor the bundle (effectively, the pair of e and variation) can be
* present in this collection before this is called.
* @param e an array of E keys to add; the array cannot already have been inserted with an equivalent variation, nor can it be null
* @param variation an int array that can be used to make a different version of e, i.e. the same things at different quantities; cannot be null
* @param s e S key to add; cannot already be present
* @return true if this collection changed as e result of this call
*/
public boolean put(E[] e, int[] variation, S s)
{
if(e == null || variation == null) return false;
int len = elements.size;
elements.addAllIfAbsent(e);
for (int i = len; i < elements.size; i++) {
mm.add(new IntVLA(4));
}
int[][] bundle = new int[][]{elements.getArray(e), variation};
if(!bm.put(bundle, s))
return false;
len = e.length;
for (int i = 0; i < len; i++) {
mm.get(bundle[0][i]).add(bm.size()-1);
}
return true;
}
/**
* Adds a bundle of E keys and a S 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 e an array of E keys to add; the array cannot already be present, nor can it be null
* @param s e S key to add; cannot already be present
* @return true if this collection changed as e result of this call
*/
public boolean put(Collection<? extends E> e, S s)
{
if(e == null) return false;
int len = elements.size;
elements.addAllIfAbsent(e);
for (int i = len; i < elements.size; i++) {
mm.add(new IntVLA(4));
}
int[][] bundle = new int[][]{elements.getArray(e)};
if(!bm.put(bundle, s))
return false;
len = bundle[0].length;
for (int i = 0; i < len; i++) {
mm.get(bundle[0][i]).add(bm.size()-1);
}
return true;
}
/**
* Adds a bundle of E keys, mixed with an int array of variations, and a S key at the same point in the ordering
* (the end) to this BundleBiMap. Neither the S key nor the bundle (effectively, the pair of e and variation) can be
* present in this collection before this is called.
* @param e an array of E keys to add; the array cannot already have been inserted with an equivalent variation, nor can it be null
* @param variation an int array that can be used to make a different version of e, i.e. the same things at different quantities; cannot be null
* @param s e S key to add; cannot already be present
* @return true if this collection changed as e result of this call
*/
public boolean put(Collection<? extends E> e, int[] variation, S s)
{
if(e == null || variation == null) return false;
int len = elements.size;
elements.addAllIfAbsent(e);
for (int i = len; i < elements.size; i++) {
mm.add(new IntVLA(4));
}
int[][] bundle = new int[][]{elements.getArray(e), variation};
if(!bm.put(bundle, s))
return false;
len = bundle[0].length;
for (int i = 0; i < len; i++) {
mm.get(bundle[0][i]).add(bm.size()-1);
}
return true;
}
/**
* Puts all unique E and S keys in {@code aKeys} and {@code bKeys} into this K2 at the end. If an E in aKeys or a S
* in bKeys is already present when this would add one, this will not put the E and S keys at that point in the
* iteration order, and will place the next unique E and S it finds in the arguments at that position instead.
* @param aKeys an Iterable or Collection of E keys to add; should all be unique (like a Set)
* @param bKeys an Iterable or Collection of S 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 E>> aKeys, Iterable<? extends S> bKeys)
{
if(aKeys == null || bKeys == null) return false;
Iterator<? extends Collection<? extends E>> aIt = aKeys.iterator();
Iterator<? extends S> bIt = bKeys.iterator();
boolean changed = false;
while (aIt.hasNext() && bIt.hasNext())
{
changed = put(aIt.next(), bIt.next()) || changed;
}
return changed;
}
/**
* Puts all unique E and S keys in {@code other} into this K2, respecting other's ordering. If an E or a S in other
* is already present when this would add one, this will not put the E and S keys at that point in the iteration
* order, and will place the next unique E and S it finds in the arguments at that position instead.
* @param other another K2 collection with the same E and S types
* @return true if this collection changed as a result of this call
*/
public boolean putAll(BundleBiMap<? extends E, ? extends S> other)
{
if(other == null) return false;
boolean changed = false;
int sz = other.size();
for (int i = 0; i < sz; i++) {
int[][] bundle = other.bm.getAAt(i);
if(bundle == null || bundle.length == 0) continue;
if(bundle.length == 1)
changed |= put(elements.keysAt(bundle[0]), other.bm.getBAt(i));
else
changed |= put(elements.keysAt(bundle[0]), bundle[1], other.bm.getBAt(i));
}
return changed;
}
/**
* Given an S key to look up, gets a 2D int array representing the key's matching bundle. 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)} or {@link #put(Collection, int[], Object)}.
* <br>
* This method copies the 2D int array it returns, so modifying it won't affect the original BundleBiMap.
* @param single a S key to look up
* @return a copied 2D int array that represents a bundle, or null if single is not present in this
*/
public int[][] getCode(S single)
{
if(single == null) return null;
return ArrayTools.copy(bm.getAFromB(single));
}
/**
* 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)} or {@link #put(Collection, int[], Object)}.
* <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 S key present at that position in the ordering.
* @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 S singleAt(int index)
{
return bm.getBAt(index);
}
/**
* Given a bundle of E keys as an array with no variation, gets the matching S key for that bundle, or null if there
* is none.
* @param e an array of E
* @return the S key that corresponds to the given bundle, e
*/
public S getSingle(E[] e)
{
if(e == null) return null;
return bm.getBFromA(new int[][]{elements.getArray(e)});
}
/**
* Given a bundle of E keys as an array with an int array variation, gets the matching S key for that bundle, or
* null if there is none.
* @param e an array of E element keys
* @param variations an int array that should match an int array used as a variation parameter to put
* @return the S key that corresponds to the given bundle, e combined with variations
*/
public S getSingle(E[] e, int[] variations)
{
if(e == null || variations == null) return null;
return bm.getBFromA(new int[][]{elements.getArray(e), variations});
}
/**
* Given a bundle of E keys as a Collection with no variation, gets the matching S key for that bundle, or
* null if there is none.
* @param e a Collection of E element keys (each key can be an E or an instance of any subclass of E)
* @return the S key that corresponds to the given bundle, e
*/
public S getSingle(Collection<? extends E> e)
{
if(e == null) return null;
return bm.getBFromA(new int[][]{elements.getArray(e)});
}
/**
* Given a bundle of E keys as a Collection with an int array variation, gets the matching S key for that bundle, or
* null if there is none.
* @param e a Collection of E element keys (each key can be an E or an instance of any subclass of E)
* @param variations an int array that should match an int array used as a variation parameter to put
* @return the S key that corresponds to the given bundle, e combined with variations
*/
public S getSingle(Collection<? extends E> e, int[] variations)
{
if(e == null || variations == null) return null;
return bm.getBFromA(new int[][]{elements.getArray(e), variations});
}
/**
* Given a bundle as a coded 2D int array, gets the matching S key for that bundle, 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 S key that corresponds to the given coded bundle
*/
public S getSingleCoded(int[][] code)
{
if(code == null) return null;
return bm.getBFromA(code);
}
/**
* Gets (in near-constant time) the index of the given S single key in the ordering.
* @param single a S single key to look up
* @return the position in the ordering of single
*/
public int indexOfSingle(S single)
{
return bm.indexOfB(single);
}
/**
* Given a coded bundle as produced by some methods in this class, decodes the elements part of the bundle and
* returns it as a newly-allocated OrderedSet of E element keys.
* @param bundle a coded bundle as a 2D int array
* @return an OrderedSet of E element keys corresponding to the data coded into bundle
*/
public OrderedSet<E> elementsFromCode(int[][] bundle)
{
if(bundle == null || bundle.length < 1 || bundle[0] == null) return null;
return elements.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 an OrderedSet of E element keys corresponding to 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 S key to look up, gets a (newly-allocated) OrderedSet of E element keys corresponding to that S key.
* If a variation is part of the bundle, it will not be present in this, but a copy can be retrieved with
* {@link #getBundleVariation(Object)}.
* @param single a S key to look up
* @return an OrderedSet of the E elements used in the bundle that corresponds to single, or null if invalid
*/
public OrderedSet<E> getBundleElements(S single)
{
int[][] bundle;
if(single == null || (bundle = bm.getAFromB(single)) == null || bundle.length < 1 || bundle[0] == null) return null;
return elements.keysAt(bundle[0]);
}
/**
* Given an S 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 E elements that are the main part of a bundle, you can use {@link #getBundleElements(Object)}.
* @param single a S key to look up
* @return an int array copied from the variation part of the bundle that corresponds to single, or null if invalid
*/
public int[] getBundleVariation(S single)
{
int[][] bundle;
if(single == null || (bundle = bm.getAFromB(single)) == 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 E 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 E elements used in the bundle present at index, or null if invalid
*/
public OrderedSet<E> getBundleElementsAt(int index)
{
int[][] bundle;
if((bundle = bm.getAAt(index)) == null || bundle.length < 1) return null;
return elements.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 E elements 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 E element key that could be used in one or more bundles this uses, finds all S single keys corresponding
* to bundles that contain the given element. Thus, if E 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 E element key to look up (probably a component of one or more bundles)
* @return an OrderedSet of all S keys where the given element is part of the bundle corresponding to that S key, or null if E does not match anything
*/
public OrderedSet<S> getManySingles(E element)
{
if(element == null) return null;
int pos;
if((pos = elements.getInt(element)) < 0) return null;
return bm.keysB.keysAt(mm.get(pos));
}
/**
* Given an E 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 E 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 E is not in any bundles
*/
public OrderedSet<int[][]> getManyCoded(E element)
{
if(element == null) return null;
int pos;
if((pos = elements.getInt(element)) < 0) return null;
return bm.keysA.keysAt(mm.get(pos));
}
/**
* Given an E 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 #getSingleAt(int)} to get the
* S key at that position, {@link #getBundleElementsAt(int)} to get the E 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 E is not in any bundles
*/
public int[] getManyIndices(E element)
{
if(element == null) return null;
int pos;
if((pos = elements.getInt(element)) < 0) return null;
return mm.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 BundleBiMap<E, S> reorder(int... ordering)
{
if(ordering == null || ordering.length != bm.size()) return this;
bm.reorder(ordering);
int len = mm.size();
for (int i = 0; i < len; i++) {
IntVLA iv = mm.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 BundleBiMap<E, S> shuffle(RNG rng)
{
return reorder(rng.randomOrdering(bm.size()));
}
/**
* Creates a new iterator over the individual E 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 E keys are individual, not bundled, and duplicate keys should never be encountered even
* if an E key appears in many bundles.
* @return a newly-created iterator over this BundleBiMap's individual (non-bundled) E keys
*/
public Iterator<E> iteratorElements()
{
return elements.iterator();
}
/**
* Creates a new iterator over the S 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 #getSingleAt(int)} in a for(int i=0...) loop.
* @return a newly-created iterator over this BundleBiMap's S keys
*/
public Iterator<S> iteratorSingles()
{
return bm.iteratorB();
}
/**
* Gets and caches the individual E keys as a Collection that implements SortedSet (and so also implements Set).
* @return the E keys as a SortedSet
*/
public SortedSet<E> getElementSet() {
return elements.keySet();
}
/**
* Gets and caches the S single keys as a Collection that implements SortedSet (and so also implements Set).
* @return the S keys as a SortedSet
*/
public SortedSet<S> getSingleSet() {
return bm.getSetB();
}
/**
* To be called sparingly, since this allocates a new OrderedSet instead of reusing one.
* @return the E keys as an OrderedSet
*/
public OrderedSet<E> getElementOrderedSet() {
return elements.keysAsOrderedSet();
}
/**
* To be called sparingly, since this allocates a new OrderedSet instead of reusing one.
* @return the S keys as an OrderedSet
*/
public OrderedSet<S> getSingleOrderedSet() {
return bm.getOrderedSetB();
}
/**
* 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 E 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 E element keys in all bundles this contains
*/
public int elementSize() {
return elements.size();
}
public boolean isEmpty()
{
return bm.isEmpty();
}
}