promql/functions.go
func funcHoltWinters(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
	mat := vals[0].(Matrix)

	// The smoothing factor argument.
	sf := vals[1].(Vector)[0].V

	// The trend factor argument.
	tf := vals[2].(Vector)[0].V

	// Sanity check the input.
	if sf <= 0 || sf >= 1 {
		panic(errors.Errorf("invalid smoothing factor. Expected: 0 < sf < 1, got: %f", sf))
	}
	if tf <= 0 || tf >= 1 {
		panic(errors.Errorf("invalid trend factor. Expected: 0 < tf < 1, got: %f", tf))
	}

	var l int
	for _, samples := range mat {
		l = len(samples.Points)

		// Can't do the smoothing operation with less than two points.
		if l < 2 {
			continue
		}

		var s0, s1, b float64
		// Set initial values.
		s1 = samples.Points[0].V
		b = samples.Points[1].V - samples.Points[0].V

		// Run the smoothing operation.
		var x, y float64
		for i := 1; i < l; i++ {

			// Scale the raw value against the smoothing factor.
			x = sf * samples.Points[i].V

			// Scale the last smoothed value with the trend at this point.
			b = calcTrendValue(i-1, sf, tf, s0, s1, b)
			y = (1 - sf) * (s1 + b)

			s0, s1 = s1, x+y
		}

		enh.out = append(enh.out, Sample{
			Point: Point{V: s1},
		})
	}

	return enh.out
}
func funcHoltWinters(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
	mat := vals[0].(Matrix)

	// The smoothing factor argument.
	sf := vals[1].(Vector)[0].V

	// The trend factor argument.
	tf := vals[2].(Vector)[0].V

	// Sanity check the input.
	if sf <= 0 || sf >= 1 {
		panic(errors.Errorf("invalid smoothing factor. Expected: 0 < sf < 1, got: %f", sf))
	}
	if tf <= 0 || tf >= 1 {
		panic(errors.Errorf("invalid trend factor. Expected: 0 < tf < 1, got: %f", tf))
	}

	var l int
	for _, samples := range mat {
		l = len(samples.Points)

		// Can't do the smoothing operation with less than two points.
		if l < 2 {
			continue
		}

		var s0, s1, b float64
		// Set initial values.
		s1 = samples.Points[0].V
		b = samples.Points[1].V - samples.Points[0].V

		// Run the smoothing operation.
		var x, y float64
		for i := 1; i < l; i++ {

			// Scale the raw value against the smoothing factor.
			x = sf * samples.Points[i].V

			// Scale the last smoothed value with the trend at this point.
			b = calcTrendValue(i-1, sf, tf, s0, s1, b)
			y = (1 - sf) * (s1 + b)

			s0, s1 = s1, x+y
		}

		enh.out = append(enh.out, Sample{
			Point: Point{V: s1},
		})
	}

	return enh.out
}
// Holt-Winters is similar to a weighted moving average, where historical data has exponentially less influence on the current data.
// Holt-Winter also accounts for trends in data. The smoothing factor (0 < sf < 1) affects how historical data will affect the current
// data. A lower smoothing factor increases the influence of historical data. The trend factor (0 < tf < 1) affects
// how trends in historical data will affect the current data. A higher trend factor increases the influence.
// of trends. Algorithm taken from https://en.wikipedia.org/wiki/Exponential_smoothing titled: "Double exponential smoothing".