promql/quantile.go
func bucketQuantile(q float64, buckets buckets) float64 {
	if q < 0 {
		return math.Inf(-1)
	}
	if q > 1 {
		return math.Inf(+1)
	}
	sort.Sort(buckets)
	if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
		return math.NaN()
	}

	buckets = coalesceBuckets(buckets)
	ensureMonotonic(buckets)

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

	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)*(rank/count)
}
func bucketQuantile(q float64, buckets buckets) float64 {
	if q < 0 {
		return math.Inf(-1)
	}
	if q > 1 {
		return math.Inf(+1)
	}
	sort.Sort(buckets)
	if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
		return math.NaN()
	}

	buckets = coalesceBuckets(buckets)
	ensureMonotonic(buckets)

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

	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)*(rank/count)
}
func bucketQuantile ( q float64 , buckets buckets ) float64 { if q < 0 { return math . Inf ( - 1 ) } if q > 1 { return math . Inf ( + 1 ) } sort . Sort ( buckets ) if ! math . IsInf ( buckets [ len ( buckets ) - 1 ] . upperBound , + 1 ) { return math . NaN ( ) } buckets = coalesceBuckets ( buckets ) ensureMonotonic ( buckets ) if len ( buckets ) < 2 { return math . NaN ( ) } 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 ) * ( 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.
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 .