@Test
  public void serialization() {
    int size = COLLISIONS.size();
    for (int i = 1; i < size; i++) {
      MutableSet<Integer> set =
          UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, SIZE)
              .withAll(COLLISIONS.subList(0, i));
      Verify.assertPostSerializedEqualsAndHashCode(set);
      set.add(null);
      Verify.assertPostSerializedEqualsAndHashCode(set);
    }

    UnifiedSetWithHashingStrategy<Integer> nullBucketZero =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY, null, COLLISION_1, COLLISION_2);
    Verify.assertPostSerializedEqualsAndHashCode(nullBucketZero);

    UnifiedSetWithHashingStrategy<Integer> simpleSetWithNull =
        UnifiedSetWithHashingStrategy.newSetWith(INTEGER_HASHING_STRATEGY, null, 1, 2);
    Verify.assertPostSerializedEqualsAndHashCode(simpleSetWithNull);

    UnifiedSetWithHashingStrategy<Person> people =
        UnifiedSetWithHashingStrategy.newSet(LAST_NAME_HASHING_STRATEGY, PEOPLE);
    Verify.assertPostSerializedEqualsAndHashCode(people);
    // Testing the hashingStrategy is serialized correctly by making sure it is still hashing by
    // last name
    Verify.assertSetsEqual(LAST_NAME_HASHED_SET.castToSet(), people.withAll(PEOPLE.castToList()));
  }
  @Test
  public void put() {
    int size = MORE_COLLISIONS.size();
    for (int i = 1; i <= size; i++) {
      Pool<Integer> unifiedSet =
          UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 1)
              .withAll(MORE_COLLISIONS.subList(0, i - 1));
      Integer newValue = MORE_COLLISIONS.get(i - 1);

      Assert.assertSame(newValue, unifiedSet.put(newValue));
      //noinspection UnnecessaryBoxing,CachedNumberConstructorCall,BoxingBoxedValue
      Assert.assertSame(newValue, unifiedSet.put(new Integer(newValue)));
    }

    // assert that all redundant puts into a each position of chain bucket return the original
    // element added
    Pool<Integer> set =
        UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 4)
            .with(COLLISION_1, COLLISION_2, COLLISION_3, COLLISION_4);
    for (int i = 0; i < set.size(); i++) {
      Integer value = COLLISIONS.get(i);
      Assert.assertSame(value, set.put(value));
    }

    // force rehashing at each step of putting a new colliding entry
    for (int i = 0; i < COLLISIONS.size(); i++) {
      Pool<Integer> pool =
          UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, i)
              .withAll(COLLISIONS.subList(0, i));
      if (i == 2) {
        pool.put(Integer.valueOf(1));
      }
      if (i == 4) {
        pool.put(Integer.valueOf(1));
        pool.put(Integer.valueOf(2));
      }
      Integer value = COLLISIONS.get(i);
      Assert.assertSame(value, pool.put(value));
    }

    // cover one case not covered in the above: a bucket with only one entry and a low capacity
    // forcing a rehash
    // set up a chained bucket
    Pool<Integer> pool =
        UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 2)
            .with(COLLISION_1, COLLISION_2);
    // clear the bucket to one element
    pool.removeFromPool(COLLISION_2);
    // increase the occupied count to the threshold
    pool.put(Integer.valueOf(1));
    pool.put(Integer.valueOf(2));

    // put the colliding value back and force the rehash
    Assert.assertSame(COLLISION_2, pool.put(COLLISION_2));

    // put chained items into a pool without causing a rehash
    Pool<Integer> olympicPool = UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY);
    Assert.assertSame(COLLISION_1, olympicPool.put(COLLISION_1));
    Assert.assertSame(COLLISION_2, olympicPool.put(COLLISION_2));
  }
  @Test
  public void removeFromPool() {
    Pool<Integer> unifiedSet =
        UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 8).withAll(COLLISIONS);
    COLLISIONS.reverseForEach(
        each -> {
          Assert.assertNull(unifiedSet.removeFromPool(null));
          Assert.assertSame(each, unifiedSet.removeFromPool(each));
          Assert.assertNull(unifiedSet.removeFromPool(each));
          Assert.assertNull(unifiedSet.removeFromPool(null));
          Assert.assertNull(unifiedSet.removeFromPool(COLLISION_10));
        });

    Assert.assertEquals(UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY), unifiedSet);

    COLLISIONS.forEach(
        Procedures.cast(
            each -> {
              Pool<Integer> unifiedSet2 =
                  UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 8)
                      .withAll(COLLISIONS);

              Assert.assertNull(unifiedSet2.removeFromPool(null));
              Assert.assertSame(each, unifiedSet2.removeFromPool(each));
              Assert.assertNull(unifiedSet2.removeFromPool(each));
              Assert.assertNull(unifiedSet2.removeFromPool(null));
              Assert.assertNull(unifiedSet2.removeFromPool(COLLISION_10));
            }));

    // search a chain for a non-existent element
    Pool<Integer> chain =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY, COLLISION_1, COLLISION_2, COLLISION_3, COLLISION_4);
    Assert.assertNull(chain.removeFromPool(COLLISION_5));

    // search a deep chain for a non-existent element
    Pool<Integer> deepChain =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY,
            COLLISION_1,
            COLLISION_2,
            COLLISION_3,
            COLLISION_4,
            COLLISION_5,
            COLLISION_6,
            COLLISION_7);
    Assert.assertNull(deepChain.removeFromPool(COLLISION_8));

    // search for a non-existent element
    Pool<Integer> empty =
        UnifiedSetWithHashingStrategy.newSetWith(INTEGER_HASHING_STRATEGY, COLLISION_1);
    Assert.assertNull(empty.removeFromPool(COLLISION_2));
  }
  @Override
  @Test
  public void toArray() {
    super.toArray();

    int size = COLLISIONS.size();
    for (int i = 1; i < size; i++) {
      MutableSet<Integer> set =
          UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, SIZE)
              .withAll(COLLISIONS.subList(0, i));
      Object[] objects = set.toArray();
      Assert.assertEquals(set, UnifiedSet.newSetWith(objects));
    }

    MutableSet<Integer> deepChain =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY,
            COLLISION_1,
            COLLISION_2,
            COLLISION_3,
            COLLISION_4,
            COLLISION_5,
            COLLISION_6);
    Assert.assertArrayEquals(
        new Integer[] {
          COLLISION_1, COLLISION_2, COLLISION_3, COLLISION_4, COLLISION_5, COLLISION_6
        },
        deepChain.toArray());

    MutableSet<Integer> minimumChain =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY, COLLISION_1, COLLISION_2);
    minimumChain.remove(COLLISION_2);
    Assert.assertArrayEquals(new Integer[] {COLLISION_1}, minimumChain.toArray());

    MutableSet<Integer> set =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY, COLLISION_1, COLLISION_2, COLLISION_3, COLLISION_4);
    Integer[] target = {
      Integer.valueOf(1),
      Integer.valueOf(1),
      Integer.valueOf(1),
      Integer.valueOf(1),
      Integer.valueOf(1),
      Integer.valueOf(1)
    };
    Integer[] actual = set.toArray(target);
    ArrayIterate.sort(actual, actual.length, Comparators.safeNullsHigh(Integer::compareTo));
    Assert.assertArrayEquals(
        new Integer[] {COLLISION_1, 1, COLLISION_2, COLLISION_3, COLLISION_4, null}, actual);
  }
  @Override
  @Test
  public void add() {
    super.add();

    // force rehashing at each step of adding a new colliding entry
    for (int i = 0; i < COLLISIONS.size(); i++) {
      UnifiedSetWithHashingStrategy<Integer> unifiedSet =
          UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, i, 0.75f)
              .withAll(COLLISIONS.subList(0, i));
      if (i == 2) {
        unifiedSet.add(Integer.valueOf(1));
      }
      if (i == 4) {
        unifiedSet.add(Integer.valueOf(1));
        unifiedSet.add(Integer.valueOf(2));
      }
      Integer value = COLLISIONS.get(i);
      Assert.assertTrue(unifiedSet.add(value));
    }

    // Rehashing Case A: a bucket with only one entry and a low capacity forcing a rehash, where the
    // trigging element goes in the bucket
    // set up a chained bucket
    UnifiedSetWithHashingStrategy<Integer> caseA =
        UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 2)
            .with(COLLISION_1, COLLISION_2);
    // clear the bucket to one element
    caseA.remove(COLLISION_2);
    // increase the occupied count to the threshold
    caseA.add(Integer.valueOf(1));
    caseA.add(Integer.valueOf(2));

    // add the colliding value back and force the rehash
    Assert.assertTrue(caseA.add(COLLISION_2));

    // Rehashing Case B: a bucket with only one entry and a low capacity forcing a rehash, where the
    // triggering element is not in the chain
    // set up a chained bucket
    UnifiedSetWithHashingStrategy<Integer> caseB =
        UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, 2)
            .with(COLLISION_1, COLLISION_2);
    // clear the bucket to one element
    caseB.remove(COLLISION_2);
    // increase the occupied count to the threshold
    caseB.add(Integer.valueOf(1));
    caseB.add(Integer.valueOf(2));

    // add a new value and force the rehash
    Assert.assertTrue(caseB.add(3));
  }
  @Test
  public void get() {
    UnifiedSetWithHashingStrategy<Integer> set =
        UnifiedSetWithHashingStrategy.newSet(INTEGER_HASHING_STRATEGY, SIZE).withAll(COLLISIONS);
    set.removeAll(COLLISIONS);
    for (Integer integer : COLLISIONS) {
      Assert.assertNull(set.get(integer));
      Assert.assertNull(set.get(null));
      set.add(integer);
      //noinspection UnnecessaryBoxing,CachedNumberConstructorCall,BoxingBoxedValue
      Assert.assertSame(integer, set.get(new Integer(integer)));
    }
    Assert.assertEquals(COLLISIONS.toSet(), set);

    // the pool interface supports getting null keys
    UnifiedSetWithHashingStrategy<Integer> chainedWithNull =
        UnifiedSetWithHashingStrategy.newSetWith(INTEGER_HASHING_STRATEGY, null, COLLISION_1);
    Verify.assertContains(null, chainedWithNull);
    Assert.assertNull(chainedWithNull.get(null));

    // getting a non-existent from a chain with one slot should short-circuit to return null
    UnifiedSetWithHashingStrategy<Integer> chainedWithOneSlot =
        UnifiedSetWithHashingStrategy.newSetWith(
            INTEGER_HASHING_STRATEGY, COLLISION_1, COLLISION_2);
    chainedWithOneSlot.remove(COLLISION_2);
    Assert.assertNull(chainedWithOneSlot.get(COLLISION_2));
  }