using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EvoBoat.EvoAlg;

namespace NumTest
{
    using NOP = NumOptProblem<VecSolution<EvoBoat.EvoAlg.Vector, double>>;

    public static class YaoTestProblems
    {
        public static NOP[] Problems;
        public static int D = 30;
        
        static YaoTestProblems()
        {
            Problems = new NOP[]{ SphereProblem, Schwefel2Problem, Schwefel3Problem, 
                Schwefel4Problem, RosenbrockProblem, StepProblem, QuarticProblem, SchwefelProblem, 
                RastriginProblem, AckleyProblem, GriewankProblem, Penalized1Problem, Penalized2Problem, 
                ShekelProblem, KowalikProblem, SixHumpProblem, BraninProblem, GoldsteinProblem,
                Hartman1Problem, Hartman2Problem, ShekelFamily1Problem, ShekelFamily2Problem, ShekelFamily3Problem
            };
        }

        public static Func<Vector, double> Sphere = (v) => v.Sum(x => x * x);
        /// <summary>
        /// f1 of Yao
        /// </summary>
        public static NOP SphereProblem = new NOP(Sphere, Vector.Create(D, -100), Vector.Create(D, 100), "Sphere Problem");

        public static Func<Vector, double> Schwefel2 = (v) => (v.Sum(x => Math.Abs(x)) + Math.Abs(v.Prod()));
        /// <summary>
        /// f2 of Yao
        /// </summary>
        public static NOP Schwefel2Problem = new NOP(Schwefel2, Vector.Create(D, -10), Vector.Create(D, 10), "Schwefel Problem 2.22");

        public static Func<Vector, double> Schwefel3 = (v) =>
        {
            double pow = 0;
            for (int i = 0; i < v.Length; i++)
                for (int j = 0; j <= i; j++)
                    pow += (v[j] * v[j]);
            return pow;
        };
        /// <summary>
        /// f3 of Yao
        /// </summary>
        public static NOP Schwefel3Problem = new NOP(Schwefel3, Vector.Create(D, -100), Vector.Create(D, 100), "Schwefel Problem 1.2");

        public static Func<Vector, double> Schwefel4 = (v) => v.Max(x => Math.Abs(x));
        /// <summary>
        /// f4 of Yao
        /// </summary>
        public static NOP Schwefel4Problem = new NOP(Schwefel4, Vector.Create(D, -100), Vector.Create(D, 100), "Schwefel Problem 2.21");

        public static double Rosenbrock(Vector v)
        {
            double pow = 0, d1, d2;
            for (int i = 1; i < v.Length; i++)
            {
                d1 = v[i] - v[i - 1] * v[i - 1];
                d2 = v[i - 1] - 1.0;
                pow += (100.0 * d1 * d1 + d2 * d2);
            }
            return pow;
        }
        /// <summary>
        /// f5 of Yao
        /// </summary>
        public static NOP RosenbrockProblem = new NOP(Rosenbrock, Vector.Create(D, -30), Vector.Create(D, 30), "Generalized Rosenbrock Problem");

        public static Func<Vector, double> Step = (v) => v.Sum(x => (Math.Floor(x + 0.5)) * (Math.Floor(x + 0.5)));
        /// <summary>
        /// f6 of Yao
        /// </summary>
        /// </summary>
        public static NOP StepProblem = new NOP(Step, Vector.Create(D, -100), Vector.Create(D, 100), "Step Problem");

        public static double QuarticWithNoise(Vector v)
        {
            double pow = 0;
            for (int i = 0; i < v.Length; i++)
                pow += (i + 1) * v[i] * v[i] * v[i] * v[i];
            return pow + (new Random()).NextDouble();
        }
        /// <summary>
        /// f7 of Yao
        /// </summary>
        public static NOP QuarticProblem = new NOP(QuarticWithNoise, Vector.Create(D, -1.28), Vector.Create(D, 1.28), "Quartic Problem with Noise");

        public static Func<Vector, double> GeneralizedSchwefel = (v) => (12569486.618173008 * v.Length - v.Sum(x => x * 30000.0 * Math.Sin(Math.Sqrt(Math.Abs(x))))) / 30000.0;
        /// <summary>
        /// f8 of Yao
        /// </summary>
        /// </summary>
        public static NOP SchwefelProblem = new NOP(GeneralizedSchwefel, Vector.Create(D, -500), Vector.Create(D, 500), "Generalized Schwefel Problem 2.26");

        public static Func<Vector, double> Rastrigin = (v) => v.Sum(x => x * x - 10 * Math.Cos(2 * Math.PI * x) + 10);
        /// <summary>
        /// f9 of Yao
        /// </summary>
        public static NOP RastriginProblem = new NOP(Rastrigin, Vector.Create(D, -5.12), Vector.Create(D, 5.12), "Generalized Rastrigin Problem");
        
        public static Func<Vector, double> Ackley = (v) => (-20.0 * Math.Exp(-0.2 * Math.Sqrt(Sphere(v) / v.Length)) - Math.Exp(v.Sum(x => Math.Cos(2.0 * Math.PI * x)) / v.Length) + 20 + Math.E);
        /// <summary>
        /// f10 of Yao
        /// </summary>
        public static NOP AckleyProblem = new NOP(Ackley, Vector.Create(D, -32), Vector.Create(D, 32), "Ackley Problem");

        public static double Griewank(Vector v)
        {
            double pow = 0, value = 1;
            for (int i = 0; i < v.Length; i++)
            {
                pow += v[i] * v[i] / 4000;
                value *= Math.Cos(v[i] / Math.Sqrt(i + 1));
            }
            return pow - value + 1;
        }
        /// <summary>
        /// f11 of Yao
        /// </summary>
        public static NOP GriewankProblem = new NOP(Griewank, Vector.Create(D, -600), Vector.Create(D, 600), "Generalized Griewank Function");

        static double GP_u(double x, double a)
        {
            if (x > a)
                return 100 * (x - a) * (x - a) * (x - a) * (x - a);
            else if (x < -a)
                return 100 * (-x - a) * (-x - a) * (-x - a) * (-x - a);
            else
                return 0.0;
        }
        static double GP_y(double x)
        {
            return 0.25 * (x + 1.0);
        }
        public static double GeneralizedPenalized1(Vector v)
        {
            double d1 = 0, d2 = 0, t1, t2;
            for (int i = 0; i < v.Length - 1; i++)
            {
                t1 = GP_y(v[i]);
                t2 = Math.Sin(Math.PI * GP_y(v[i + 1] + 1.0));
                d1 += t1 * t1 * (1.0 + 10.0 * t2 * t2);
            }
            for (int i = 0; i < v.Length; i++)
            {
                d2 += GP_u(v[i], 10);
            }
            t1 = Math.Sin(Math.PI * (1.0 + GP_y(v[0])));
            t2 = GP_y(v[v.Length - 1]);
            return Math.PI * (10 * t1 * t1 + d1 + t2 * t2) / 30.0 + d2;
        }
        /// <summary>
        /// f12 of Yao
        /// </summary>
        public static NOP Penalized1Problem = new NOP(GeneralizedPenalized1, Vector.Create(D, -50), Vector.Create(D, 50), "Generalized Penalized Problem1");

        public static double GeneralizedPenalized2(Vector v)
        {
            double d1 = 0, d2 = 0, t1, t2, t3;
            for (int i = 0; i < v.Length - 1; i++)
            {
                t1 = (v[i] - 1.0);
                t2 = Math.Sin(3.0 * Math.PI * (v[i + 1]));
                d1 += t1 * t1 * (1.0 + 10.0 * t2 * t2);
            }
            for (int i = 0; i < v.Length; i++)
            {
                d2 += GP_u(v[i], 5);
            }
            t1 = Math.Sin(3.0 * Math.PI * v[0]);
            t2 = (v[v.Length - 1] - 1.0);
            t3 = Math.Sin(2.0 * Math.PI * v[v.Length - 1]);
            return 0.1 * (t1 * t1 + d1 + t2 * t2 * (1.0 + t3 * t3)) + d2;
        }
        /// <summary>
        /// f13 of Yao
        /// </summary>
        public static NOP Penalized2Problem = new NOP(GeneralizedPenalized2, Vector.Create(D, -50), Vector.Create(D, 50), "Generalized Penalized Problem2");

        public static double Shekel_Foxholes(Vector v)
        {
            int[,] a = { { -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32 },
                         { -32, -32, -32, -32, -32, -16, -16, -16, -16, -16, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32 } };
            double d1 = 0, d2, tmp;
            for (int j = 0; j < 25; j++)
            {
                d2 = 0;
                for (int i = 0; i < 2; i++)
                {
                    tmp = (v[i] - a[i, j]);
                    d2 += (tmp * tmp * tmp * tmp * tmp * tmp);
                }
                d1 += 1.0 / (1.0 + j + d2);
            }
            return 1.0 / (0.002 + d1) - 0.9980038377944495559;
        }
        /// <summary>
        /// f14 of Yao
        /// </summary>
        public static NOP ShekelProblem = new NOP(Shekel_Foxholes, Vector.Create(2, -65.536), Vector.Create(2, 65.536), "Shekel Foxholes Problem");

        public static double Kowalik(Vector v)
        {
            double result = 0, tmp;
            double[] a = { 0.1957, 0.1947, 0.1735, 0.16, 0.0844, 0.0627, 0.0456, 0.0342, 0.0323, 0.0235, 0.0246 };
            double[] b = { 0.25, 0.5, 1, 2, 4, 6, 8, 10, 12, 14, 16 };
            for (int i = 0; i < 11; i++)
            {
                tmp = a[i] - v[0] * (1.0 + b[i] * v[1]) / (1.0 + b[i] * v[2] + b[i] * b[i] * v[3]);
                result += (tmp * tmp);
            }
            return result - 0.0003074859878056042;
        }
        /// <summary>
        /// f15 of Yao
        /// </summary>
        public static NOP KowalikProblem = new NOP(Kowalik, Vector.Create(4, -5.0), Vector.Create(4, 5), "Kowalik Problem");

        public static Func<Vector, double> SixHump = (v) =>
        {
            double d1 = v[0] * v[0], d2 = v[1] * v[1];
            return 4.0 * d1 - 2.1 * d1 * d1 + d1 * d1 * d1 / 3.0 + v[0] * v[1] - 4.0 * d2 + 4 * d2 * d2 + 1.03162845349;
        };
        /// <summary>
        /// f16 of Yao
        /// </summary>
        public static NOP SixHumpProblem = new NOP(SixHump, Vector.Create(2, -5.0), Vector.Create(2, 5.0), "Six-Hump Camel-Back Problem");

        public static Func<Vector, double> Branin = (v) =>
        {
            double tmp = (v[1] - 5.1 * v[0] * v[0] / (4.0 * Math.PI * Math.PI) + 5.0 * v[0] / Math.PI - 6.0);
            return tmp * tmp + 10.0 * (1.0 - 1.0 / (8.0 * Math.PI)) * Math.Cos(v[0]) + 10;// -0.3978866423 - 0.000000715429738;
        };
        /// <summary>
        /// f17 of Yao
        /// </summary>
        public static NOP BraninProblem = new NOP(Branin, new double[] { -5.0, 0.0 }, new double[] { 10.0, 15.0 }, "Branin Problem");

        public static Func<Vector, double> Goldstein_Price = (v) => (1.0 + (v[0] + v[1] + 1) * (v[0] + v[1] + 1) * (19 - 14 * v[0] + 3 * v[0] * v[0] - 14 * v[1] + 6 * v[0] * v[1] + 3 * v[1] * v[1])) * (30.0 + (2 * v[0] - 3 * v[1]) * (2 * v[0] - 3 * v[1]) * (18 - 32 * v[0] + 12 * v[0] * v[0] + 48 * v[1] - 36 * v[0] * v[1] + 27 * v[1] * v[1])) - 3;
        /// <summary>
        /// f18 of Yao
        /// </summary>
        public static NOP GoldsteinProblem = new NOP(Goldstein_Price, Vector.Create(2, -2.0), Vector.Create(2, 2.0), "Goldstein-Price Problem");

        public static double Hartman1(Vector v)
        {
            double result = 0, d;
            double[,] a = { { 3, 10, 30 }, { 0.1, 10, 35 }, { 3, 10, 30 }, { 0.1, 10, 35 } };
            double[] c = { 1, 1.2, 3, 3.2 };
            double[,] p = { { 0.3689, 0.1170, 0.2673 }, { 0.4699, 0.4387, 0.7470 }, { 0.1091, 0.8732, 0.5547 }, { 0.03815, 0.5743, 0.8828 } };
            for (int i = 0; i < 4; i++)
            {
                d = 0;
                for (int j = 0; j < 3; j++)
                    d += a[i, j] * (v[j] - p[i, j]) * (v[j] - p[i, j]);
                result += c[i] * Math.Pow(Math.E, -d);
            }
            return 3.86278214783 - result;
        }
        /// <summary>
        /// f19 of Yao
        /// </summary>
        public static NOP Hartman1Problem = new NOP(Hartman1, 3, "Hartman Problem1");

        public static double Hartman2(Vector v)
        {
            double result = 0, d;
            double[,] a = { { 10, 3, 17, 3.5, 1.7, 8 }, { 0.05, 10, 17, 0.1, 8, 14 }, { 3, 3.5, 1.7, 10, 17, 8 }, { 17, 8, 0.05, 10, 0.1, 14 } };
            double[] c = { 1, 1.2, 3, 3.2 };
            double[,] p = { { 0.1312, 0.1696, 0.5569, 0.0124, 0.8283, 0.5886 }, { 0.2329, 0.4135, 0.8307, 0.3736, 0.1004, 0.9991 }, { 0.2348, 0.1415, 0.3522, 0.2883, 0.3047, 0.6650 }, { 0.4047, 0.8828, 0.8732, 0.5743, 0.1091, 0.0381 } };
            for (int i = 0; i < 4; i++)
            {
                d = 0;
                for (int j = 0; j < 6; j++)
                    d += a[i, j] * (v[j] - p[i, j]) * (v[j] - p[i, j]);
                result += c[i] * Math.Pow(Math.E, -d);
            }
            return 3.321995171584243 - result;
        }
        /// <summary>
        /// f20 of Yao
        /// </summary>
        public static NOP Hartman2Problem = new NOP(Hartman2, 6, "Hartman Problem2");

        //coefficients for Shekel's Family functions
        static double[,] aSh = { { 4, 4, 4, 4 }, { 1, 1, 1, 1 }, { 8, 8, 8, 8 }, { 6, 6, 6, 6 }, { 3, 7, 3, 7 }, { 2, 9, 2, 9 }, { 5, 5, 3, 3 }, { 8, 1, 8, 1 }, { 6, 2, 6, 2 }, { 7, 3.6, 7, 3.6 } };
        static double[] cSh = { 0.1, 0.2, 0.2, 0.4, 0.4, 0.6, 0.3, 0.7, 0.5, 0.5 };
        static double ShekelFamily(Vector v, int m)
        {
            double result = 0, d;
            for (int i = 0; i < m; i++)
            {
                d = 0;
                for (int j = 0; j < v.Length; j++)
                    d += (v[j] - aSh[i, j]) * (v[j] - aSh[i, j]);
                result += 1.0 / (d + cSh[i]);
            }
            return -result;
        }

        public static Func<Vector, double> ShekelFamily1 = v => ShekelFamily(v, 5) + 10.15319968;
        /// <summary>
        /// f21 of Yao
        /// </summary>
        public static NOP ShekelFamily1Problem = new NOP(ShekelFamily1, Vector.Create(4, 0), Vector.Create(4, 10), "Shekel Family Problem1");

        public static Func<Vector, double> ShekelFamily2 = v => ShekelFamily(v, 7) + 10.402940567;
        /// <summary>
        /// f22 of Yao
        /// </summary>
        public static NOP ShekelFamily2Problem = new NOP(ShekelFamily2, Vector.Create(4, 0), Vector.Create(4, 10), "Shekel Family Problem2");

        public static Func<Vector, double> ShekelFamily3 = v => ShekelFamily(v, 10) + 10.53640981669205;
        /// <summary>
        /// f23 of Yao
        /// </summary>
        public static NOP ShekelFamily3Problem = new NOP(ShekelFamily3, Vector.Create(4, 0), Vector.Create(4, 10), "Shekel Family Problem3");
    }
}
