/*************************************************************************
* Project: Library of Evolutionary Algoriths
*************************************************************************
* Author: Changhe Li & Ming Yang
* Email: changhe.lw@google.com Or yangming0702@gmail.com
* Language: C++
*************************************************************************
*  This file is part of EAlib. This library is free software;
*  you can redistribute it and/or modify it under the terms of the
*  GNU General Public License as published by the Free Software
*  Foundation; either version 2, or (at your option) any later version.
*************************************************************************/
// Created: 21 September 2011
// Last modified:
/*Storn, R. and Price, K. (1997), "Differential Evolution - A Simple and Efficient Heuristic for Global Optimization over Continuous Spaces",
Journal of Global Optimization, 11, pp. 341-359*/
#ifndef DEPOPULATION_H
#define DEPOPULATION_H
#include "../Population.h"
#include "../Optima.h"
#include "../../Problems/DOPs/DynamicProblem.h"
template <class T>
class DEPopulation: public Population<T>{
    public:
    float  m_F, m_CR;
    DEMutationStratgy m_mutStrategy;
    DEPopulation():Population<T>(),m_F(0.5),m_CR(0.1),m_mutStrategy(DE_rand_1){
		defaultParameter();
    }
    virtual ~DEPopulation(){}

    DEPopulation(const int rPopsize,bool mode=true):Population<T>(rPopsize,mode),m_F(0.5),m_CR(0.1),m_mutStrategy(DE_rand_1){
		defaultParameter();
    }
	DEPopulation(DEPopulation<T> &s):Population<T>(s){

	m_CR=s.m_CR;
        m_F=s.m_F;
        m_mutStrategy=s.m_mutStrategy;

	}
	DEPopulation(Group<T> &g):Population<T>(g),m_F(0.5),m_CR(0.1),m_mutStrategy(DE_rand_1){
		defaultParameter();
	}
	DEPopulation(Chromosome & center, double radius,const int rPopsize,bool mode=true):Population<T>(center,radius,rPopsize,mode),m_F(0.5),m_CR(0.1),m_mutStrategy(DE_rand_1){
		defaultParameter();
	}

	void setMutationStrategy(DEMutationStratgy rS){
        m_mutStrategy=rS;
	}

    DEPopulation & operator=( DEPopulation & s){
        if(this==&s) return *this;

		Population<T>::operator=(s);

        m_CR=s.m_CR;
        m_F=s.m_F;
        m_mutStrategy=s.m_mutStrategy;

        return *this;

    }
    void mutate(const int idx){
        int *a=new int[this->m_popsize];
        gInitializeRandomArray(a,this->m_popsize);
        int j=0;
        while(a[j]!=idx){j++;}
        int r1,r2,r3,r4,r5;
        r1=a[(j+1)%this->m_popsize];
        r2=a[(j+2)%this->m_popsize];
        r3=a[(j+3)%this->m_popsize];
        r4=a[(j+4)%this->m_popsize];
        r5=a[(j+5)%this->m_popsize];

        switch(m_mutStrategy){
            case DE_rand_1:
                this->mp_pop[idx].mutate(m_F,&this->mp_pop[r1].m_pself,&this->mp_pop[r2].m_pself,&this->mp_pop[r3].m_pself);
                break;
            case DE_best_1:
                this->mp_pop[idx].mutate(m_F,&this->m_best,&this->mp_pop[r1].m_pself,&this->mp_pop[r2].m_pself);
                break;
            case DE_targetToBest_1:
                this->mp_pop[idx].mutate(m_F,&this->mp_pop[idx].m_pself,&this->m_best,&this->mp_pop[idx].m_pself,&this->mp_pop[r1].m_pself,&this->mp_pop[r2].m_pself);
                break;
            case DE_best_2:
                this->mp_pop[idx].mutate(m_F,&this->m_best,&this->mp_pop[r1].m_pself,&this->mp_pop[r2].m_pself,&this->mp_pop[r3].m_pself,&this->mp_pop[r4].m_pself);
                break;
            case DE_rand_2:
                this->mp_pop[idx].mutate(m_F,&this->mp_pop[r1].m_pself,&this->mp_pop[r2].m_pself,&this->mp_pop[r3].m_pself,&this->mp_pop[r4].m_pself,&this->mp_pop[r5].m_pself);
                break;
			case DE_randToBest_1:
				this->mp_pop[idx].mutate(m_F,&this->mp_pop[r1].m_pself,&this->m_best,&this->mp_pop[r1].m_pself,&this->mp_pop[r2].m_pself,&this->mp_pop[r3].m_pself);
				break;
			case DE_targetToRand_1:
				this->mp_pop[idx].mutate(m_F,&this->mp_pop[idx].m_pself,&this->mp_pop[r1].m_pself,&this->mp_pop[idx].m_pself,&this->mp_pop[r2].m_pself,&this->mp_pop[r3].m_pself);
				break;
        }
        delete []a;
    }

    int evolve(){
        if(this->m_popsize<1){
            return 0;
        }

        for(int i=0;i<this->m_popsize;i++){
            mutate(i);
            this->mp_pop[i].recombine(m_CR);
        }

        this->updateIDnIndex();
        for(int i=0;i<this->m_popsize;i++){
            this->mp_pop[i].select();

            if(this->mp_pop[i].m_pself>(this->m_best)) {
                this->m_best=this->mp_pop[i].m_pself;
            }
			if(gIsTerminate()) return 0;
            if(gIsDynamicAlg()&&Global::gp_problem->getEvaluations()%(dynamic_cast<DynamicProblem*>(Global::gp_problem)->getChangeFre())==0){
                if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getFlagDimensionChange()){

                if(this->msp_subPop.size()>1){
                    if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getDirDimensionChange()==true)  Population<T>::increaseDimensionAll();
                    else Population<T>::decreaseDimensionAll();
                }else{
                    if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getDirDimensionChange()==true)  this->increaseDimension();
                    else this->decreaseDimension();
                }
                    return 2;
                }

                if(this->msp_subPop.size()>1){
                    this->updateMemoryAll();
                }else{
                    this->updateMemory();
                }
                return 1;
            }

        }
        this->m_evoNum++;
        return 0;
    }
    void setParmeter(const float cr, const float f){
        m_CR=cr;
        m_F=f;
    }
	void defaultParameter(){
		m_CR=0.6;
		m_F=0.5;
		m_mutStrategy=DE_best_2;
	}
};

#endif // DEPOPULATION_H


