func bucketQuantile(q float64, buckets buckets) float64 {
	if q < 0 {
		return math.Inf(-1)
	}
	if q > 1 {
		return math.Inf(+1)
	}
	if len(buckets) < 2 {
		return math.NaN()
	}
	sort.Sort(buckets)
	if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
		return math.NaN()
	}
	ensureMonotonic(buckets)
	rank := q * buckets[len(buckets)-1].count
	b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
	if b == len(buckets)-1 {
		return buckets[len(buckets)-2].upperBound
	}
	if b == 0 && buckets[0].upperBound <= 0 {
		return buckets[0].upperBound
	}
	var (
		bucketStart float64
		bucketEnd   = buckets[b].upperBound
		count       = buckets[b].count
	)
	if b > 0 {
		bucketStart = buckets[b-1].upperBound
		count -= buckets[b-1].count
		rank -= buckets[b-1].count
	}
	return bucketStart + (bucketEnd-bucketStart)*float64(rank/count)
}
bucketQuantile calculates the quantile 'q' based on the given buckets. The
buckets will be sorted by upperBound by this function (i.e. no sorting
needed before calling this function). The quantile value is interpolated
assuming a linear distribution within a bucket. However, if the quantile
falls into the highest bucket, the upper bound of the 2nd highest bucket is
returned. A natural lower bound of 0 is assumed if the upper bound of the
lowest bucket is greater 0. In that case, interpolation in the lowest bucket
happens linearly between 0 and the upper bound of the lowest bucket.
However, if the lowest bucket has an upper bound less or equal 0, this upper
bound is returned if the quantile falls into the lowest bucket.

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 fewer than 2 elements, NaN is returned.

If the highest bucket is not +Inf, NaN is returned.

If q<0, -Inf is returned.

If q>1, +Inf is returned.
package promql
import (
	"math"
	"sort"
	"github.com/prometheus/prometheus/pkg/labels"
)
var excludedLabels = []string{
	labels.MetricName,
	labels.BucketLabel,
}
type bucket struct {
	upperBound float64
	count      float64
}
type buckets []bucket
func (b buckets) Len() int           { return len(b) }
func (b buckets) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound }
type metricWithBuckets struct {
	metric  labels.Labels
	buckets buckets
}