public static Xor16 construct(final long[] keys, final int offset, final int length) {
    final int arrayLength = getArrayLength(length);
    final int blockLength = arrayLength / HASHES;
    final long[] reverseOrder = new long[length];
    final byte[] reverseH = new byte[length];
    int reverseOrderPos;
    // constant seed - reproducible JARs (initial seed just a random number)
    long seed = 1354212L;
    do {
      seed = mix(seed);
      byte[] t2count = new byte[arrayLength];
      long[] t2 = new long[arrayLength];
      for (int i = offset; i < length; i++) {
        long k = keys[i];
        for (int hi = 0; hi < HASHES; hi++) {
          int h = getHash(blockLength, k, seed, hi);
          t2[h] ^= k;
          if (t2count[h] > 120) {
            throw new IllegalArgumentException();
          }
          t2count[h]++;
        }
      }
      int[] alone = new int[arrayLength];
      int alonePos = 0;
      reverseOrderPos = 0;
      for (int nextAloneCheck = 0; nextAloneCheck < arrayLength; ) {
        while (nextAloneCheck < arrayLength) {
          if (t2count[nextAloneCheck] == 1) {
            alone[alonePos++] = nextAloneCheck;
          }
          nextAloneCheck++;
        }
        while (alonePos > 0) {
          int i = alone[--alonePos];
          if (t2count[i] == 0) {
            continue;
          }
          long k = t2[i];
          byte found = -1;
          for (int hi = 0; hi < HASHES; hi++) {
            int h = getHash(blockLength, k, seed, hi);
            int newCount = --t2count[h];
            if (newCount == 0) {
              found = (byte)hi;
            }
            else {
              if (newCount == 1) {
                alone[alonePos++] = h;
              }
              t2[h] ^= k;
            }
          }
          reverseOrder[reverseOrderPos] = k;
          reverseH[reverseOrderPos] = found;
          reverseOrderPos++;
        }
      }
    }
    while (reverseOrderPos != length);
    short[] fingerprints = new short[arrayLength];
    for (int i = reverseOrderPos - 1; i >= 0; i--) {
      long k = reverseOrder[i];
      int found = reverseH[i];
      int change = -1;
      long hash = hash64(k, seed);
      int xor = fingerprint(hash);
      for (int hi = 0; hi < HASHES; hi++) {
        int h = getHash(blockLength, k, seed, hi);
        if (found == hi) {
          change = h;
        }
        else {
          xor ^= fingerprints[h];
        }
      }
      fingerprints[change] = (short)xor;
    }
    return new Xor16(fingerprints, blockLength, seed);
  }
public static Xor16 construct(final long[] keys, final int offset, final int length)