in src/main/java/org/apache/datasketches/tdigest/TDigestDouble.java [237:284]
public double getQuantile(final double rank) {
if (isEmpty()) { throw new SketchesStateException(QuantilesAPI.EMPTY_MSG); }
if (Double.isNaN(rank)) { throw new SketchesArgumentException("Operation is undefined for Nan"); }
if (rank < 0 || rank > 1) { throw new SketchesArgumentException("Normalized rank must be within [0, 1]"); }
compress(); // side effect
if (numCentroids_ == 1) { return centroidMeans_[0]; }
// at least 2 centroids
final double weight = rank * centroidsWeight_;
if (weight < 1) { return minValue_; }
if (weight > centroidsWeight_ - 1.0) { return maxValue_; }
final double firstWeight = centroidWeights_[0];
if (firstWeight > 1 && weight < firstWeight / 2.0) {
return minValue_ + (weight - 1.0) / (firstWeight / 2.0 - 1.0) * (centroidMeans_[0] - minValue_);
}
final double lastWeight = centroidWeights_[numCentroids_ - 1];
if (lastWeight > 1 && centroidsWeight_ - weight <= lastWeight / 2.0) {
return maxValue_ + (centroidsWeight_ - weight - 1.0) / (lastWeight / 2.0 - 1.0) * (maxValue_ - centroidMeans_[numCentroids_ - 1]);
}
// interpolate between extremes
double weightSoFar = firstWeight / 2.0;
for (int i = 0; i < numCentroids_ - 1; i++) {
final double dw = (centroidWeights_[i] + centroidWeights_[i + 1]) / 2.0;
if (weightSoFar + dw > weight) {
// the target weight is between centroids i and i+1
double leftWeight = 0;
if (centroidWeights_[i] == 1) {
if (weight - weightSoFar < 0.5) { return centroidMeans_[i]; }
leftWeight = 0.5;
}
double rightWeight = 0;
if (centroidWeights_[i + 1] == 1) {
if (weightSoFar + dw - weight <= 0.5) { return centroidMeans_[i + 1]; }
rightWeight = 0.5;
}
final double w1 = weight - weightSoFar - leftWeight;
final double w2 = weightSoFar + dw - weight - rightWeight;
return weightedAverage(centroidMeans_[i], w1, centroidMeans_[i + 1], w2);
}
weightSoFar += dw;
}
final double w1 = weight - centroidsWeight_ - centroidWeights_[numCentroids_ - 1] / 2.0;
final double w2 = centroidWeights_[numCentroids_ - 1] / 2.0 - w1;
return weightedAverage(centroidWeights_[numCentroids_ - 1], w1, maxValue_, w2);
}