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

namespace EvoBoat.EvoAlg.PSO
{
    /// <summary>
    /// Framework of comprehensive learning particle swarm optimization
    /// </summary>
    public abstract class CLPSOAlg<P, S, R, e> : PSOAlg<P, S, R, e>
        where P : IProblem<S, R>
        where S : VecSolution<R, e>, ICloneable<S>, new()
        where R : IVector<e>, ICloneable<R>
    {
        protected double[] pc; //self-learning probabilities

        public CLPSOAlg() : base()
        {
            Name = "CLPSO";
        }

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

        public override void SetParameters()
        {
            CRate1 = 1.49445;
        }

        public override void InitPop()
        {
            base.InitPop();
            pc = new double[Size];
            for (int i = 0; i < Size; i++)
                pc[i] = 0.05 + 0.45 * (Math.Exp(10 * i / (Size - 1)) - 1) / (Math.Exp(10) - 1);
        }

        public override void AdjustVelocity(S sol, int index)
        {
            bool learnOther = false;
            S examplar;
            for (int d = 0; d < Problem.Dimension; d++)
            {
                if (Rand.NextDouble() < pc[index])
                    examplar = pbests[index];
                else
                {
                    Tuple<int, int> t = RandomSelectTwoIndicies(pop.Length, index);
                    examplar = (pbests[t.Item1].Fit < pbests[t.Item2].Fit) ? pbests[t.Item1] : pbests[t.Item2];
                    learnOther = true;
                }
                velocities[index][d] = wInertia * velocities[index][d] + Rand.NextDouble() * CRate1 * Difference(examplar.Content[d], sol.Content[d]);
            }
            if (!learnOther)
            {
                int d = Rand.Next(Problem.Dimension);
                velocities[index][d] = wInertia * velocities[index][d] + Rand.NextDouble() * CRate1 * Difference(pop[RandomSelectIndex(pop.Length, index)].Content[d], sol.Content[d]);
            }
        }
    }
}
