// Copyright 2023 wanderer <a_mirre at utb dot cz>
// SPDX-License-Identifier: GPL-3.0-or-later

package cec2020

import "math"

// Rastrigin calculates the value of the Rastrigin function for x.
func Rastrigin(x []float64) float64 {
	var sum float64

	for i := range x {
		sum += math.Pow(x[i], 2) - (10 * math.Cos(2*math.Pi*x[i])) + 10
	}

	return sum
}

// HighConditionedElliptic is the "High Conditioned Elliptic Function" of
// CEC2020.
func HighConditionedElliptic(x []float64) float64 {
	var sum float64

	// float64 version of the length of x.
	fnx := float64(len(x))

	for i := range x {
		sum += math.Pow(math.Pow(10, 6), float64(i-1)/(fnx-1)) * math.Pow(x[i], 2)
	}

	return sum
}

// HGBat is the "HGBat Function" of CEC2020.
func HGBat(x []float64) float64 {
	var sum1 float64

	var sum2 float64

	for i := range x {
		sum1 += math.Pow(x[i], 2)

		sum2 += x[i]
	}

	// float64 version of the length of x.
	fnx := float64(len(x))

	return math.Pow(math.Abs(math.Pow(sum1, 2)-math.Pow(sum2, 2)), 0.5) + (((0.5 * sum1) + sum2) / (fnx + 0.5))
}

// Rosenbrock is the "Rosenbrock's Function" of CEC2020.
// ref: https://infinity77.net/global_optimization/test_functions_nd_R.html#go_benchmark.Rosenbrock
func Rosenbrock(x []float64) float64 {
	var sum float64

	for i := 0; i < len(x)-1; i++ {
		sum += 100*(math.Pow(math.Pow(x[i], 2)-x[i+1], 2)) + math.Pow(x[i]-1, 2)
	}

	return sum
}

// Griewank is the "Griewank's Function" of CEC2020.
// ref: https://www.sfu.ca/~ssurjano/griewank.html
func Griewank(x []float64) float64 {
	var sum float64

	var prod float64

	for i := range x {
		sum += math.Pow(x[i], 2) / 4000
		// use i+1 in sqrt so that we don't get Nan.
		prod *= math.Cos(x[i] / math.Sqrt(float64(i+1)))
	}

	return sum - prod + 1
}

// Ackley is the "Ackley's Function" of CEC2020.
func Ackley(x []float64) float64 {
	var sum1 float64

	var sum2 float64

	for i := range x {
		sum1 += math.Pow(x[i], 2)

		sum2 += math.Cos(2 * math.Pi * x[i])
	}

	// float64 version of the length of x.
	fnx := float64(len(x))

	return -20*math.Exp(-0.2*math.Sqrt((1/fnx)*sum1)) - math.Exp((1/fnx)*sum2) + 20 + math.E
}

// Happycat is the "Happycat Function" of CEC2020.
func Happycat(x []float64) float64 {
	var sum1 float64

	var sum2 float64

	var sum3 float64

	// float64 version of the length of x.
	fnx := float64(len(x))

	for i := range x {
		xipow2 := math.Pow(x[i], 2)

		sum1 += xipow2 - fnx

		sum2 += xipow2

		sum3 += x[i]
	}

	return math.Pow(math.Abs(sum1), 0.25) + (((0.5 * sum2) + sum3) / (fnx + 0.5))
}

// Discus is the "Discus Function" of CEC2020.
func Discus(x []float64) float64 {
	var sum float64

	nx := len(x)

	for i := 1; i < nx; i++ {
		sum += math.Pow(x[i], 2)
	}

	return sum + (1000000 * math.Pow(x[0], 2))
}

// SchwefelModified is the "Modified Schwefel's Function" of CEC2020 with summation
// of g(zi) where zi = xi + 4.209687462275036e+002 and
// g(zi) = zi * sin(|zi|^(1/2)) ... if |zi| <= 500,
// g(zi) = (500-mod(zi,500)) * sin(sqrt(|500-mod(zi,500)|)) - (zi-500^2)/10000D ... if zi > 500,
// g(zi) = (mod(|zi|,500)-500) * sin(sqrt(|mod(|zi|,500)-500|)) - (zi-500^2)/10000D ... if zi < -500.
func SchwefelModified(x []float64) float64 {
	var sum float64

	// float64 version of the length of x.
	fnx := float64(len(x))

	for i := range x {
		zi := x[i] + 4.209687462275036e+002

		switch {
		case zi > 500:
			// g(zi)
			sum += (500-math.Mod(zi, 500))*math.Sin(math.Sqrt(math.Abs(500-math.Mod(zi, 500)))) - (math.Pow(zi-500.0, 2.0) - 10000*fnx)

		case zi < -500:
			// g(zi)
			sum += (math.Mod(math.Abs(zi), 500)-500)*math.Sin(math.Sqrt(math.Abs(math.Mod(math.Abs(zi), 500)-500))) - (math.Pow(zi-500, 2) - 10000*fnx)

		case math.Abs(zi) <= 500:
			// g(zi)
			sum += zi * math.Sin(math.Pow(math.Abs(zi), 0.5))
		}
	}

	return 418.9829*fnx - sum
}

// Schaffer is the Schaffer's function.
func Schaffer(x, y float64) float64 {
	return 0.5 + (math.Pow(math.Sin(math.Sqrt(math.Pow(x, 2)+math.Pow(y, 2))-0.5), 2) / (1 + 0.001*(math.Pow(x, 2)+math.Pow(y, 2))))
}

// SchafferExpanded is the "Expanded Schaffer's Function" of CEC2020.
func SchafferExpanded(x []float64) float64 {
	var sum float64

	nx := len(x)

	for i := range x {
		sum += Schaffer(x[i], x[i%nx])
	}

	return sum
}

// Weierstrass is the "Weierstrass Function" of CEC2020 with a=0.5, b=3 and
// kmax=20.
func Weierstrass(x []float64) float64 {
	a := 0.5
	b := 3.0
	kmax := 20.0
	// float64 version of the length of x.
	fnx := float64(len(x))

	var sum1 float64

	var sum2 float64

	for i := range x {
		var ksum float64

		for k := 0.0; k < kmax; k++ {
			ksum += math.Pow(a, k) * math.Cos(2*math.Pi*math.Pow(b, k)*(x[i]+0.5))
		}

		sum1 += ksum
	}

	for k := 0.0; k < kmax; k++ {
		sum2 += math.Pow(a, k) * math.Cos(2*math.Pi*math.Pow(b, k)*0.5)
	}

	return sum1 - (fnx * sum2)
}
