public boolean put(int key,
                     int value) {
    checkNotNoValue("key", key);
    checkNotNoValue("value", value);
    int capacity = capacity();
    int startIndex = Math.abs(key % capacity);
    int firstTombstoneIndex = -1;
    for (int probe = 0; probe < capacity; probe++) {
      int slotIndex = (startIndex + probe) % capacity;
      int slotKey = table[slotIndex * 2];
      int slotValue = table[slotIndex * 2 + 1];
      if (slotKey == key && slotValue == value) {
        return false;//record already here, nothing to add
      }

      if (slotKey == NO_VALUE) {
        if (slotValue != NO_VALUE) {
          //slot removed -> remember index, but continue lookup
          if (firstTombstoneIndex == -1) {
            firstTombstoneIndex = slotIndex;
          }
        }
        else {
          //(NO_VALUE, NO_VALUE) -> free slot -> end of probing sequence, no (key, value) found -> insert it:
          int insertionIndex = firstTombstoneIndex >= 0 ? firstTombstoneIndex : slotIndex;
          table[insertionIndex * 2] = key;
          table[insertionIndex * 2 + 1] = value;

          aliveValues++;
          filledSlots++;
          rehashIfNeeded();

          return true;
        }
      }
    }

    //probing sequence went through all the table: i.e. table is full -- but maybe there are tombstones to replace?

    if (aliveValues == 0) {
      //If there is 0 alive records => it is OK to clear all the tombstones.
      // We can't clear all tombstones while alive entries exist because such a cleaning breaks lookup: we treat
      // free slots and tombstones differently during the probing -- continue to probe over tombstones, but stop
      // on free slots. Converting tombstone to free slot could stop the probing earlier than it should stop, thus
      // making some existing entries unreachable.
      // But if there are no alive entries anymore -- we _can_ clear everything without breaking anything.
      Arrays.fill(table, NO_VALUE);
      filledSlots = 0;

      put(key, value);
    }

    if (firstTombstoneIndex != -1) {
      //replace a tombstone:
      table[firstTombstoneIndex * 2] = key;
      table[firstTombstoneIndex * 2 + 1] = value;

      aliveValues++;
      filledSlots++;
      rehashIfNeeded();
    }


    //Table must be resized well before such a condition occurs!
    throw new AssertionError(
      "Table is full: all " + capacity + " items were traversed, but no free slot found " +
      "table(" + table.length + "): .aliveEntries=" + aliveValues + ", filledEntries=" + filledSlots + ", " +
      (table.length <= 64 ? Arrays.toString(table) : "")
    );
  }
public boolean put(int key,
                     int value)