using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EvoBoat.EvoAlg.DE
{
    public abstract class DEAlg<P, S, R, e> : EAlg<P, S, R, e>
        where P : IProblem<S, R>
        where S : VecSolution<R, e>, new()
        where R : IVector<e>, ICloneable<R>
    {
        public double ScalingF { get; set; }
        public double CrossRate { get; set; }

        public DEAlg(int seed = 0)
            : base(seed)
        {
            Name = "DE";
        }

        public DEAlg(P problem, int seed = 0)
            : base(problem, seed)
        {
            Name = "DE";
        }

        public override void SetParameters()
        {
            ScalingF = 0.5;
            CrossRate = 0.9;
        }

        public override void Calculate()
        { }

        public abstract double Difference(e pos1, e pos2);
        public abstract e Increase(e pos, double v);
        public abstract void Repair(S sol, int index, int d, e oldv);

        public virtual S DiffEvolve(S sol,int index)
        {
            S sol1 = new S() { Content = sol.Content.Clone() };
            Tuple<int, int, int> ind = RandomSelectThreeIndices(pop.Length, index);
            int r1 = ind.Item1, r2 = ind.Item2, r3 = ind.Item3;
            int j = Rand.Next(Problem.Dimension);
            for (int d = 0; d < Problem.Dimension; d++)
            {
                if (Rand.NextDouble() < CrossRate || j == d)
                {
                    e olde = sol1.Content[d];
                    sol1.Content[d] = Increase(pop[r1].Content[d], ScalingF * Difference(pop[r2].Content[d], pop[r3].Content[d]));
                    Repair(sol1, index, d, olde);
                }
            }
            this.Evaluate(sol1);
            return sol1;
        }

        public override void Evolve()
        {
            S sol1;
            for (int i = 0; i < pop.Length; i++)
            {
                sol1 = DiffEvolve(pop[i], i);
                if (sol1.Fit < pop[i].Fit)
                {
                    pop[i] = sol1;
                    if (pop[i].Fit < best.Fit)
                        best = pop[i];
                }
            }
            iter++;
        }
    }
}
