public static BloomSpecification computeBloomSpec(int maxBucketsPerElement, double maxFalsePosProb) {
    assert maxBucketsPerElement >= 1;
    assert maxBucketsPerElement <= probs.length - 1;
    int maxK = probs[maxBucketsPerElement].length - 1;

    // Handle the trivial cases
    if (maxFalsePosProb >= probs[minBuckets][minK]) {
      return new BloomSpecification(2, optKPerBuckets[2]);
    }
    if (maxFalsePosProb < probs[maxBucketsPerElement][maxK]) {
      throw new UnsupportedOperationException(
          String.format("Unable to satisfy %s with %s buckets per element", maxFalsePosProb, maxBucketsPerElement));
    }

    // First find the minimal required number of buckets:
    int bucketsPerElement = 2;
    int K = optKPerBuckets[2];
    while (probs[bucketsPerElement][K] > maxFalsePosProb) {
      bucketsPerElement++;
      K = optKPerBuckets[bucketsPerElement];
    }
    // Now that the number of buckets is sufficient, see if we can relax K
    // without losing too much precision.
    while (probs[bucketsPerElement][K - 1] <= maxFalsePosProb) {
      K--;
    }

    return new BloomSpecification(K, bucketsPerElement);
  }
Given a maximum tolerable false positive probability, compute a Bloom
specification which will give less than the specified false positive rate,
but minimize the number of buckets per element and the number of hash
functions used.  Because bandwidth (and therefore total bitvector size)
is considered more expensive than computing power, preference is given
to minimizing buckets per element rather than number of hash functions.

@param maxBucketsPerElement The maximum number of buckets available for the filter.
@param maxFalsePosProb The maximum tolerable false positive rate.
@return A Bloom Specification which would result in a false positive rate
less than specified by the function call
@throws UnsupportedOperationException if a filter satisfying the parameters cannot be met
ambry-utils/src/main/java/com/github/ambry/utils/BloomCalculations.java