/*************************************************************************
* 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 Oct. 2011
// Last modified:
#include "ESCA.h"
#ifdef DEMON_EALIB
#include "../../../buffer/buffer.h"
#endif
ESCA::ESCA(const int rSize):Algorithm(ALG_ESCA,(char *)"Evolutionary Swarm Cooperative Algorithm")
{
    //ctor
	mp_swarm=new SPSOGBest(rSize);
	//mp_swarm->initializePara(1.0,2.0,2.0);
	//for(int i=0;i<mp_swarm->m_popsize;i++) mp_swarm->mp_pop[i].setVmax(-1,1);

	mp_mCRDE=new  CRDE(rSize);
	mp_CRDE=new CRDE(rSize);
	
	m_sigma=10;
	m_theta=1.;
}

ESCA::~ESCA()
{
    //dtor
	delete mp_swarm;
	delete mp_mCRDE;
	delete mp_CRDE;
}
void ESCA::setPara(float rTheta, int rSigma){
	m_theta=rTheta;
	m_sigma=rSigma;
}
void ESCA::changeDimension(){
	if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getDirDimensionChange()==true){
		mp_swarm->increaseDimension();
		mp_mCRDE->increaseDimension();
		mp_CRDE->increaseDimension();
	}else{
		mp_swarm->decreaseDimension();
		mp_mCRDE->decreaseDimension();
		mp_CRDE->decreaseDimension();
	}
}
void ESCA::updateMemory(){
	mp_swarm->updateMemory();
	mp_mCRDE->updateMemory();
	mp_CRDE->updateMemory();
}
int ESCA::evolve(){

	Chromosome x;
    x=mp_mCRDE->m_best;

	if(!(mp_mCRDE->m_best== mp_swarm->m_best) && mp_mCRDE->m_best.getDistance(mp_swarm->m_best)<m_theta){
		// reinitialize the swarm population by replacement all inidis from mCRDE
		for(int i=0;i<mp_swarm->m_popsize;i++){
			mp_swarm->mp_pop[i].initialize(&mp_mCRDE->mp_pop[i].m_pself);
		}
		mp_swarm->m_best=mp_mCRDE->m_best;
	}

	int flag;
	flag=mp_mCRDE->evolve();
	if(flag==1)		updateMemory();
	else if(flag==2) changeDimension();

	if(gIsTerminate()) return 0;

	flag=mp_swarm->evolve();
	if(flag==1)		updateMemory();
	else if(flag==2) changeDimension();
	if(gIsTerminate()) return 0;

	if(mp_mCRDE->m_evoNum%m_sigma==0){
		if(mp_mCRDE->m_best.getDistance(mp_CRDE->m_best)<m_theta){
			*mp_mCRDE=*mp_CRDE;
		}
		flag=mp_CRDE->evolve();
		if(flag==1)		updateMemory();
		else if(flag==2) changeDimension();
		if(gIsTerminate()) return 0;
	}

	double xfit=x.getObj();
	if(!dynamic_cast<DynamicProblem * >(Global::gp_problem)->predictChange(1)){
		//the detection will cost one evalution, which may trigger the enviromental change. In such case, change detection does not work
		x.evaluate();
	}


    if(flag==1&&xfit==x.getObj()){
        cout<<"detection failed"<<endl;
    }
    if(xfit!=x.getObj()){

		for(int i=0;i<mp_swarm->m_popsize;i++){
			mp_swarm->mp_pop[i].initialize(&mp_mCRDE->mp_pop[i].m_pself);
		}
		mp_swarm->m_best=mp_mCRDE->m_best;

    }

	if(mp_swarm->m_best>(mp_CRDE->m_best)){
		int idx1,idx2;
		mp_CRDE->findNearestPair(idx1,idx2);
		mp_CRDE->mp_pop[idx1].m_pself=mp_swarm->m_best;
		mp_CRDE->m_best=mp_swarm->m_best;
	}else{
		mp_swarm->m_best=mp_CRDE->m_best;
	}

	if(mp_swarm->m_best>(mp_mCRDE->m_best)){
		mp_mCRDE->m_best=mp_swarm->m_best;
	}


	return 0;

}

void ESCA::updateBuffer(Population<Individual>*pso,Population<Individual> *de1,Population<Individual> *de2){

	for(int i=0;i<mp_swarm->m_popsize;i++){
		pso->mp_pop[i]=mp_swarm->mp_pop[i];
	}
	for(int i=0;i<mp_mCRDE->m_popsize;i++){
		de1->mp_pop[i]=mp_mCRDE->mp_pop[i];
	}
	for(int i=0;i<mp_CRDE->m_popsize;i++){
		de2->mp_pop[i]=mp_CRDE->mp_pop[i];
	}

}
int ESCA::run(){

	Population<Individual> *pso;

	pso=new Population<Individual>(mp_swarm->m_popsize);

	Population<Individual> *de1,*de2;

	de1=new Population<Individual>(mp_mCRDE->m_popsize);
	de2=new Population<Individual>(mp_CRDE->m_popsize);
	
	vector<Population<Individual> *> subPop;

	updateBuffer(pso,de1,de2);

	subPop.push_back(pso);
	subPop.push_back(de1);
	subPop.push_back(de2);

     while(!gIsTerminate()){
         //cout<<Global::g_runIdx+1<<" "<<Global::gp_problem->getEvaluations()<<endl;
		#ifdef DEMON_EALIB
		 Buffer<Individual>::updateBuffer(subPop);
#endif
         evolve();
		#ifdef DEMON_EALIB
		 updateBuffer(pso,de1,de2);
#endif
     }
	#ifdef DEMON_EALIB
	 Buffer<Individual>::updateBuffer(subPop);
#endif
	
	 delete pso;
	 delete de1;
	 delete de2;
	 subPop.clear();

    return 0;

}
