/**
   * Creates a new, empty map with the specified initial capacity, load factor and concurrency
   * level.
   *
   * @param initialCapacity the initial capacity. The implementation performs internal sizing to
   *     accommodate this many elements.
   * @param loadFactor the load factor threshold, used to control resizing. Resizing may be
   *     performed when the average number of elements per bin exceeds this threshold.
   * @param concurrencyLevel the estimated number of concurrently updating threads. The
   *     implementation performs internal sizing to try to accommodate this many threads.
   * @throws IllegalArgumentException if the initial capacity is negative or the load factor or
   *     concurrencyLevel are nonpositive.
   */
  public LongConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
    if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
      throw new IllegalArgumentException();

    if (concurrencyLevel > MAX_SEGMENTS) concurrencyLevel = MAX_SEGMENTS;

    // Find power-of-two sizes best matching arguments
    int sshift = 0;
    int ssize = 1;
    while (ssize < concurrencyLevel) {
      ++sshift;
      ssize <<= 1;
    }
    segmentShift = 32 - sshift;
    segmentMask = ssize - 1;
    this.segments = Segment.newArray(ssize);

    if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY;
    int c = initialCapacity / ssize;
    if (c * ssize < initialCapacity) ++c;
    int cap = 1;
    while (cap < c) cap <<= 1;

    for (int i = 0; i < this.segments.length; ++i)
      this.segments[i] = new Segment<V>(cap, loadFactor);
  }
  /**
   * Creates a new, empty map with the specified initial capacity, load factor and concurrency
   * level.
   *
   * @param initialCapacity the initial capacity. The implementation performs internal sizing to
   *     accommodate this many elements.
   * @param loadFactor the load factor threshold, used to control resizing. Resizing may be
   *     performed when the average number of elements per bin exceeds this threshold.
   * @param concurrencyLevel the estimated number of concurrently updating threads. The
   *     implementation performs internal sizing to try to accommodate this many threads.
   * @throws IllegalArgumentException if the initial capacity is negative or the load factor or
   *     concurrencyLevel are nonpositive.
   */
  public ConcurrentHashTable(
      final FieldIndex[] index, int initialCapacity, float loadFactor, int concurrencyLevel) {
    this.startResult = RightTupleIndexHashTable.PRIME;
    for (int i = 0, length = index.length; i < length; i++) {
      this.startResult =
          RightTupleIndexHashTable.PRIME * this.startResult + index[i].getExtractor().getIndex();
    }

    switch (index.length) {
      case 0:
        throw new IllegalArgumentException(
            "FieldIndexHashTable cannot use an index[] of length  0");
      case 1:
        this.index = new SingleIndex(index, this.startResult);
        break;
      case 2:
        this.index = new DoubleCompositeIndex(index, this.startResult);
        break;
      case 3:
        this.index = new TripleCompositeIndex(index, this.startResult);
        break;
      default:
        throw new IllegalArgumentException(
            "FieldIndexHashTable cannot use an index[] of length  great than 3");
    }

    if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
      throw new IllegalArgumentException();

    if (concurrencyLevel > MAX_SEGMENTS) concurrencyLevel = MAX_SEGMENTS;

    // Find power-of-two sizes best matching arguments
    int sshift = 0;
    int ssize = 1;
    while (ssize < concurrencyLevel) {
      ++sshift;
      ssize <<= 1;
    }
    segmentShift = 32 - sshift;
    segmentMask = ssize - 1;
    this.segments = Segment.newArray(ssize);

    if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY;
    int c = initialCapacity / ssize;
    if (c * ssize < initialCapacity) ++c;
    int cap = 1;
    while (cap < c) cap <<= 1;

    for (int i = 0; i < this.segments.length; ++i)
      this.segments[i] = new Segment(this.index, cap, loadFactor);
  }