@Test
  public void test() {
    List<Integer> keys = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
      keys.add(i);
    }
    Collections.shuffle(keys);

    AVLTree<Integer, Object> t = AVLTree.create();
    for (Integer key : keys) {
      t = t.add(key);
      assertThat(t.add(key)).isSameAs(t);
    }
    assertThat(Counter.countSet(t)).isEqualTo(100);
    assertThat(Counter.countMap(t)).isEqualTo(100);
    assertThat(t.height()).isGreaterThanOrEqualTo(8).isLessThanOrEqualTo(10);

    for (Integer key : keys) {
      assertThat(t.contains(key)).isTrue();
      t = t.remove(key);
      assertThat(t.remove(key)).isSameAs(t);
    }
    assertThat(Counter.countSet(t)).isEqualTo(0);
    assertThat(Counter.countMap(t)).isEqualTo(0);
  }
 @Test
 public void no_change() {
   AVLTree<String, String> t0 = AVLTree.create();
   AVLTree<String, String> t1 = t0.put("1", "1");
   assertThat(t1.put("1", "1")).isSameAs(t1);
   assertThat(t1.remove("3")).isSameAs(t1);
   AVLTree<String, String> t2 = t0.put("2", "2");
   assertThat(t2.put("2", "2")).isSameAs(t2);
   assertThat(t2.remove("3")).isSameAs(t2);
 }
  @Test
  public void buckets() {
    Object k1 = new Key(42, "k1");
    Object k2 = new Key(42, "k2");
    Object k3 = new Key(42, "k3");
    AVLTree<Object, Object> t = AVLTree.create().put(k1, "v1").put(k2, "v2");

    assertThat(t.toString()).as("should create bucket").isEqualTo(" k2->v2 k1->v1");

    AVLTree<Object, Object> t2 = AVLTree.create().put(k2, "v2").put(k1, "v1");
    assertThat(t2.toString())
        .as("toString depends on order of operations")
        .isEqualTo(" k1->v1 k2->v2");

    assertThat(t.equals(t2)).as("should compare buckets").isTrue();
    assertThat(t2.equals(t)).as("should compare buckets").isTrue();

    assertThat(t.hashCode())
        .isEqualTo(
            ((31 * k1.hashCode()) ^ "v1".hashCode()) + ((31 * k2.hashCode()) ^ "v2".hashCode()));
    assertThat(t2.hashCode())
        .as("hashCode doesn't depend on order of operations")
        .isEqualTo(t.hashCode());

    assertThat(t.get(k1)).isEqualTo("v1");
    assertThat(t.get(k2)).isEqualTo("v2");
    assertThat(t.get(k3)).as("not such key").isNull();

    assertThat(t.put(k2, "new v2").toString())
        .as("should replace head of bucket")
        .isEqualTo(" k2->new v2 k1->v1");
    assertThat(t.put(k1, "new v1").toString())
        .as("should replace element of bucket")
        .isEqualTo(" k1->new v1 k2->v2");
    assertThat(t.put(k1, "v1")).as("should not change").isSameAs(t);
    assertThat(t.put(k2, "v2")).as("should not change").isSameAs(t);
    assertThat(t.put(k3, "v3").toString())
        .as("should add to bucket")
        .isEqualTo(" k3->v3 k2->v2 k1->v1");

    assertThat(t.remove(k2).toString()).as("should remove head of bucket").isEqualTo(" k1->v1");
    assertThat(t.remove(k1).toString()).as("should remove element of bucket").isEqualTo(" k2->v2");
    assertThat(t.remove(k1).remove(k2).toString()).as("should remove bucket").isEqualTo("");
    assertThat(t.remove(k3)).as("should not change").isSameAs(t);

    HashMap<Object, Object> biConsumer = new HashMap<>();
    t.forEach((k, v) -> assertThat(biConsumer.put(k, v)).as("unique key-value").isNull());
    assertThat(biConsumer).isEqualTo(ImmutableMap.of(k1, "v1", k2, "v2"));

    HashSet<Object> consumer = new HashSet<>();
    t.forEach(k -> assertThat(consumer.add(k)).as("unique key").isTrue());
    assertThat(consumer).containsOnly(k1, k2);
  }
 @Test
 public void test_empty() {
   AVLTree<String, String> t = AVLTree.create();
   assertThat(t).as("singleton").isSameAs(AVLTree.create());
   assertThat(t.get("anything")).isNull();
   assertThat(t.remove("anything")).isSameAs(t);
   assertThat(t.toString()).isEqualTo("");
   assertThat(t.hashCode()).isEqualTo(0);
 }
 /**
  * Subtraction must not be used for comparison of keys due to possibility of integer overflow,
  * this for example will be the case for sequence below, which was generated using random number
  * generator.
  */
 @Test
 public void do_not_use_subtraction_for_comparison_of_keys() {
   Key[] keys = {
     new Key(2043979982, ""),
     new Key(-36348207, ""),
     new Key(-1864559204, ""),
     new Key(-2018458363, ""),
     new Key(-152409201, ""),
     new Key(-1786252453, ""),
     new Key(-1853960690, "")
   };
   AVLTree<Object, Object> t = AVLTree.create();
   for (Key key : keys) {
     t = t.add(key);
   }
   for (Key key : keys) {
     assertThat(t.get(key)).as("found").isNotNull();
     assertThat(t.remove(key)).as("removed").isNotSameAs(t);
   }
 }