/*
 * SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
 * SPDX-License-Identifier: Apache-2.0
 */

package z

import (
	
	
	

	
)

// Creates bounds for an histogram. The bounds are powers of two of the form
// [2^min_exponent, ..., 2^max_exponent].
func (,  uint32) []float64 {
	var  []float64
	for  := ;  <= ; ++ {
		 = append(, float64(int(1)<<))
	}
	return 
}

func ( int) []float64 {
	assert( > 4)
	 := make([]float64, )
	[0] = 1
	[1] = 2
	for  := 2;  < ; ++ {
		[] = [-1] + [-2]
	}
	return 
}

// HistogramData stores the information needed to represent the sizes of the keys and values
// as a histogram.
type HistogramData struct {
	Bounds         []float64
	Count          int64
	CountPerBucket []int64
	Min            int64
	Max            int64
	Sum            int64
}

// NewHistogramData returns a new instance of HistogramData with properly initialized fields.
func ( []float64) *HistogramData {
	return &HistogramData{
		Bounds:         ,
		CountPerBucket: make([]int64, len()+1),
		Max:            0,
		Min:            math.MaxInt64,
	}
}

func ( *HistogramData) () *HistogramData {
	if  == nil {
		return nil
	}
	return &HistogramData{
		Bounds:         append([]float64{}, .Bounds...),
		CountPerBucket: append([]int64{}, .CountPerBucket...),
		Count:          .Count,
		Min:            .Min,
		Max:            .Max,
		Sum:            .Sum,
	}
}

// Update changes the Min and Max fields if value is less than or greater than the current values.
func ( *HistogramData) ( int64) {
	if  == nil {
		return
	}
	if  > .Max {
		.Max = 
	}
	if  < .Min {
		.Min = 
	}

	.Sum += 
	.Count++

	for  := 0;  <= len(.Bounds); ++ {
		// Allocate value in the last buckets if we reached the end of the Bounds array.
		if  == len(.Bounds) {
			.CountPerBucket[]++
			break
		}

		if  < int64(.Bounds[]) {
			.CountPerBucket[]++
			break
		}
	}
}

// Mean returns the mean value for the histogram.
func ( *HistogramData) () float64 {
	if .Count == 0 {
		return 0
	}
	return float64(.Sum) / float64(.Count)
}

// String converts the histogram data into human-readable string.
func ( *HistogramData) () string {
	if  == nil {
		return ""
	}
	var  strings.Builder

	.WriteString("\n -- Histogram: \n")
	.WriteString(fmt.Sprintf("Min value: %d \n", .Min))
	.WriteString(fmt.Sprintf("Max value: %d \n", .Max))
	.WriteString(fmt.Sprintf("Count: %d \n", .Count))
	.WriteString(fmt.Sprintf("50p: %.2f \n", .Percentile(0.5)))
	.WriteString(fmt.Sprintf("75p: %.2f \n", .Percentile(0.75)))
	.WriteString(fmt.Sprintf("90p: %.2f \n", .Percentile(0.90)))

	 := len(.Bounds)
	var  float64
	for ,  := range .CountPerBucket {
		if  == 0 {
			continue
		}

		// The last bucket represents the bucket that contains the range from
		// the last bound up to infinity so it's processed differently than the
		// other buckets.
		if  == len(.CountPerBucket)-1 {
			 := uint64(.Bounds[-1])
			 := float64(*100) / float64(.Count)
			 += 
			.WriteString(fmt.Sprintf("[%s, %s) %d %.2f%% %.2f%%\n",
				humanize.IBytes(), "infinity", , , ))
			continue
		}

		 := uint64(.Bounds[])
		 := uint64(0)
		if  > 0 {
			 = uint64(.Bounds[-1])
		}

		 := float64(*100) / float64(.Count)
		 += 
		.WriteString(fmt.Sprintf("[%d, %d) %d %.2f%% %.2f%%\n",
			, , , , ))
	}
	.WriteString(" --\n")
	return .String()
}

// Percentile returns the percentile value for the histogram.
// value of p should be between [0.0-1.0]
func ( *HistogramData) ( float64) float64 {
	if  == nil {
		return 0
	}

	if .Count == 0 {
		// if no data return the minimum range
		return .Bounds[0]
	}
	 := int64(float64(.Count) * )
	for ,  := range .CountPerBucket {
		 =  - 
		if  <= 0 {
			if  == len(.Bounds) {
				break
			}
			return .Bounds[]
		}
	}
	// default return should be the max range
	return .Bounds[len(.Bounds)-1]
}

// Clear reset the histogram. Helpful in situations where we need to reset the metrics
func ( *HistogramData) () {
	if  == nil {
		return
	}

	.Count = 0
	.CountPerBucket = make([]int64, len(.Bounds)+1)
	.Sum = 0
	.Max = 0
	.Min = math.MaxInt64
}