Repository: prometheus/prometheus
File: promql/quantile.go (Go)

Code before:
//
// There are a number of special cases (once we have a way to report errors
// happening during evaluations of AST functions, we should report those
// explicitly):
//
// If 'buckets' has 0 observations, NaN is returned.
//
// If 'buckets' has fewer than 2 elements, NaN is returned.
//
// If the highest bucket is not +Inf, NaN is returned.
//
// If q==NaN, NaN is returned.
//
// If q<0, -Inf is returned.
//
// If q>1, +Inf is returned.
//
// We also return extra info, see doc for ensureMonotonicAndIgnoreSmallDeltas.
//
// Generically speaking, BucketQuantile is for calculating the
// histogram_quantile() of classic histograms. See also: HistogramQuantile
// for native histograms.
//
// BucketQuantile is exported as a useful quantile function over a set of
// given buckets. It may be used by other PromQL engine implementations.
func BucketQuantile(q float64, buckets Buckets) (res, forcedMonotonicMinBucket, forcedMonotonicMaxBucket, forcedMonotonicMaxDiff float64, forcedMonotonic, fixedPrecision bool) {
	if math.IsNaN(q) {
		res = math.NaN()
		return res, forcedMonotonicMinBucket, forcedMonotonicMaxBucket, forcedMonotonicMaxDiff, forcedMonotonic, fixedPrecision
	}
	if q < 0 {
		res = math.Inf(-1)
		return res, forcedMonotonicMinBucket, forcedMonotonicMaxBucket, forcedMonotonicMaxDiff, forcedMonotonic, fixedPrecision
	}
	if q > 1 {
		res = math.Inf(+1)
		return res, forcedMonotonicMinBucket, forcedMonotonicMaxBucket, forcedMonotonicMaxDiff, forcedMonotonic, fixedPrecision
	}
	slices.SortFunc(buckets, func(a, b Bucket) int {
		// We don't expect the bucket boundary to be a NaN.
		if a.UpperBound < b.UpperBound {
			return -1
		}
		if a.UpperBound > b.UpperBound {
			return +1
		}
		return 0
	})
	if !math.IsInf(buckets[len(buckets)-1].UpperBound, +1) {
		res = math.NaN()
		return res, forcedMonotonicMinBucket, forcedMonotonicMaxBucket, forcedMonotonicMaxDiff, forcedMonotonic, fixedPrecision

Review comment (suggestion):
Just cosmetic notes about variable naming:

Generally, I'm a fan of longer variable names to avoid confusion, but given that we use those names so often in the function and they are all local, I'd say we can go for shorter names.

On the other hand, the most important return value is abbreviated (`res`) and maybe should have a more descriptive name.

How about the following (note the changed order, which might make the meaning of the return values more obvious, despite the shorter names – also note the grouping by line breaks):


```suggestion
func BucketQuantile(q float64, buckets Buckets) (
	quantile float64,
	forcedMonotonic, fixedPrecision bool,
	minBucket, maxBucket, maxDiff float64,
) {
```

In different news, I would personally think this function is a good example where a naked `return` without the named returns would be justified. But I guess we have a lint check forbidding that, so let's not have that battle right now.

Code after:
//
// There are a number of special cases (once we have a way to report errors
// happening during evaluations of AST functions, we should report those
// explicitly):
//
// If 'buckets' has 0 observations, NaN is returned.
//
// If 'buckets' has fewer than 2 elements, NaN is returned.
//
// If the highest bucket is not +Inf, NaN is returned.
//
// If q==NaN, NaN is returned.
//
// If q<0, -Inf is returned.
//
// If q>1, +Inf is returned.
//
// We also return extra info, see doc for ensureMonotonicAndIgnoreSmallDeltas.
//
// Generically speaking, BucketQuantile is for calculating the
// histogram_quantile() of classic histograms. See also: HistogramQuantile
// for native histograms.
//
// BucketQuantile is exported as a useful quantile function over a set of
// given buckets. It may be used by other PromQL engine implementations.
func BucketQuantile(q float64, buckets Buckets) (
	quantile float64,
	forcedMonotonic, fixedPrecision bool,
	minBucket, maxBucket, maxDiff float64,
) {
	switch {
	case math.IsNaN(q):
		quantile = math.NaN()
		return quantile, forcedMonotonic, fixedPrecision, minBucket, maxBucket, maxDiff
	case q < 0:
		quantile = math.Inf(-1)
		return quantile, forcedMonotonic, fixedPrecision, minBucket, maxBucket, maxDiff
	case q > 1:
		quantile = math.Inf(+1)
		return quantile, forcedMonotonic, fixedPrecision, minBucket, maxBucket, maxDiff
	}
	slices.SortFunc(buckets, func(a, b Bucket) int {
		// We don't expect the bucket boundary to be a NaN.
		if a.UpperBound < b.UpperBound {
			return -1
		}
		if a.UpperBound > b.UpperBound {
			return +1
		}
		return 0
	})