in src/main/java/org/apache/datasketches/tdigest/TDigestDouble.java [179:230]
public double getRank(final double value) {
if (isEmpty()) { throw new SketchesStateException(QuantilesAPI.EMPTY_MSG); }
if (Double.isNaN(value)) { throw new SketchesArgumentException("Operation is undefined for Nan"); }
if (value < minValue_) { return 0; }
if (value > maxValue_) { return 1; }
if (numCentroids_ + numBuffered_ == 1) { return 0.5; }
compress(); // side effect
// left tail
final double firstMean = centroidMeans_[0];
if (value < firstMean) {
if (firstMean - minValue_ > 0) {
if (value == minValue_) { return 0.5 / centroidsWeight_; }
return (1.0 + (value - minValue_) / (firstMean - minValue_) * (centroidWeights_[0] / 2.0 - 1.0));
}
return 0; // should never happen
}
// right tail
final double lastMean = centroidMeans_[numCentroids_ - 1];
if (value > lastMean) {
if (maxValue_ - lastMean > 0) {
if (value == maxValue_) { return 1.0 - 0.5 / centroidsWeight_; }
return 1.0 - ((1.0 + (maxValue_ - value) / (maxValue_ - lastMean)
* (centroidWeights_[numCentroids_ - 1] / 2.0 - 1.0)) / centroidsWeight_);
}
return 1; // should never happen
}
int lower = BinarySearch.lowerBound(centroidMeans_, 0, numCentroids_, value);
if (lower == numCentroids_) { throw new SketchesStateException("lower == end in getRank()"); }
int upper = BinarySearch.upperBound(centroidMeans_, lower, numCentroids_, value);
if (upper == 0) { throw new SketchesStateException("upper == begin in getRank()"); }
if (value < centroidMeans_[lower]) { lower--; }
if (upper == numCentroids_ || !(centroidMeans_[upper - 1] < value)) { upper--; }
double weightBelow = 0;
int i = 0;
while (i != lower) { weightBelow += centroidWeights_[i++]; }
weightBelow += centroidWeights_[lower] / 2.0;
double weightDelta = 0;
while (i != upper) { weightDelta += centroidWeights_[i++]; }
weightDelta -= centroidWeights_[lower] / 2.0;
weightDelta += centroidWeights_[upper] / 2.0;
if (centroidMeans_[upper] - centroidMeans_[lower] > 0) {
return (weightBelow + weightDelta * (value - centroidMeans_[lower])
/ (centroidMeans_[upper] - centroidMeans_[lower])) / centroidsWeight_;
}
return (weightBelow + weightDelta / 2.0) / centroidsWeight_;
}