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}