func deltaExplicitBucketsQuantile()

in cmd/otelinmemexporter/histogram.go [60:125]


func deltaExplicitBucketsQuantile(q float64, buckets []explicitBucket) float64 {
	if math.IsNaN(q) {
		return math.NaN()
	}
	if q < 0 {
		return math.Inf(-1)
	}
	if q > 1 {
		return math.Inf(+1)
	}

	slices.SortFunc(buckets, func(a, b explicitBucket) int {
		// We don't expect the bucket boundary to be a NaN.
		return cmp.Compare(a.UpperBound, b.UpperBound)
	})

	if len(buckets) < 2 {
		return math.NaN()
	}

	// The highest bound must be +Inf, and the lowest bound must be -Inf.
	if !math.IsInf(buckets[len(buckets)-1].UpperBound, +1) {
		return math.NaN()
	}

	// Check if there are any observations.
	var observations uint64
	for _, bucket := range buckets {
		observations += bucket.Count
	}
	if observations == 0 {
		return math.NaN()
	}

	// Find the bucket that the quantile falls into.
	rank := q * float64(observations)
	var countSoFar uint64
	bucketIdx := slices.IndexFunc(buckets, func(bucket explicitBucket) bool {
		countSoFar += bucket.Count
		// Compare using `>=` instead of `>` since upper bound is inclusive.
		return float64(countSoFar) >= rank
	})

	if bucketIdx == len(buckets)-1 {
		return buckets[len(buckets)-2].UpperBound
	}
	if bucketIdx == 0 {
		return buckets[0].UpperBound
	}

	// Interpolate to get quantile in bucket.
	bucketStart := buckets[bucketIdx-1].UpperBound
	bucketEnd := buckets[bucketIdx].UpperBound
	bucketCount := buckets[bucketIdx].Count
	// How the bucket quantile is derived:
	// ==|=======|=======|=======|=======|==
	//   |       |       |       |       |
	//   | b - 2 | b - 1 |   b   | b + 1 |
	// ==|=======|=======|=======|=======|==
	// ----------------------> rank
	// --------------------------> countSoFar
	//                   |-------> bucketCount
	//                   |---> rank - (countSoFar - bucketCount)
	bucketQuantile := (rank - float64(countSoFar-bucketCount)) / float64(bucketCount)
	return bucketStart + (bucketEnd-bucketStart)*bucketQuantile
}