/*************************************************************************
* 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:
#include "SwarmLBest.h"

#ifdef DEMON_EALIB
#include "../../../buffer/buffer.h"
#endif

SwarmLBest::SwarmLBest()
{
    //ctor
}

SwarmLBest::~SwarmLBest()
{
    //dtor
    freeMemory();
}
SwarmLBest::SwarmLBest(const int rSize,bool mode):Swarm<ParticleLbest>(rSize,mode),m_impr(0){

	setID(ALG_SPSO07);
    strcpy(ma_name,"SPSO_LBEST");

    allocateMemory(m_popsize);

    m_algPar<<"Population size: "<<m_popsize;
}
SwarmLBest::SwarmLBest(SwarmLBest & s):Swarm<ParticleLbest>(s),m_impr(0){
    allocateMemory(m_popsize);
    for(int i=0;i<m_popsize;i++){
        gCopy(mpp_linkInfor[i],s.mpp_linkInfor[i],m_popsize);
    }

}
int SwarmLBest::run(){
     while(!gIsTerminate()){
        //cout<<Global::g_runIdx<<" "<<m_best.getObj()<<endl;
		#ifdef DEMON_EALIB
		 Buffer<ParticleLbest>::updateBuffer(this);
		#endif
        evolve();
    }
	 #ifdef DEMON_EALIB
	 Buffer<ParticleLbest>::updateBuffer(this);
#endif
     return 0;
}
int SwarmLBest::evolve(){

    //lbest model with ring topology
	//generate a permutation of particle index
	if(m_popsize<1) return 0;
	int r_flag=-1;
	int *rindex=new int[m_popsize];
	gInitializeRandomArray(rindex,m_popsize);
	initializeLinkInfor();

	m_impr=0;

	for(int i=0;i<m_popsize;i++){
		int l=findLbest(rindex[i]);

		if(l!=rindex[i])
		mp_pop[rindex[i]].move2007(mp_pop[rindex[i]].m_pbest,m_W,m_C1,m_C2,&mp_pop[l].m_pbest,true);
		else mp_pop[rindex[i]].move2007(mp_pop[rindex[i]].m_pbest,m_W,m_C1,m_C2,0,true);

		if(mp_pop[rindex[i]].m_pself.isValid()){
			if(mp_pop[rindex[i]].m_pself>(mp_pop[rindex[i]].m_pbest)){
				mp_pop[rindex[i]].m_pbest=mp_pop[rindex[i]].m_pself;
				if(mp_pop[rindex[i]].m_pself>(m_best)) {
					m_best=mp_pop[rindex[i]].m_pself;
					m_impr++;
				}
			}

        }
        if(gIsTerminate()) 	break ;
		if(gIsDynamicAlg()&&Global::gp_problem->getEvaluations()%(dynamic_cast<DynamicProblem*>(Global::gp_problem)->getChangeFre())==0){
			if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getFlagDimensionChange()) {r_flag=2; break;}  // dimensional change
			this->updateMemoryAll();
			{r_flag=1; break;}  // non-dimensinal change
		}

	}
	m_evoNum++;
	delete [] rindex;	
	if(r_flag==-1) 	return 0;
	else return r_flag;
}
void SwarmLBest::allocateMemory(const int size){
    mpp_linkInfor=new bool*[size];

    for(int i=0;i<size;i++) mpp_linkInfor[i]=new bool[size];

}
void SwarmLBest::freeMemory(){
    for(int i=0;i<m_popsize;i++) delete [] mpp_linkInfor[i];

    delete [] mpp_linkInfor;
    mpp_linkInfor=0;
}
void SwarmLBest::initializeLinkInfor(){
    double p=1-pow(1-(double)1/(m_popsize),3);
	for(int i=0;i<m_popsize;i++){
		if(m_impr==0){
			for(int k=0;k<m_popsize;k++){
				if(Global::gp_uniformAlg->Next()<p) mpp_linkInfor[i][k]=true;
				else
				mpp_linkInfor[i][k]=false;
			}
			mpp_linkInfor[i][i]=true;
		}

	}

}
int SwarmLBest::findLbest(const int index){
        int l=index;
		for(int k=0;k<m_popsize;k++){
			if(mpp_linkInfor[index][k]==true&&mp_pop[k].m_pbest>(mp_pop[l].m_pbest)){
				l=k;
			}
		}
		return l;

}
SwarmLBest & SwarmLBest::operator= ( SwarmLBest &s){
    if(this==&s) return * this;
    if(m_popsize!=s.m_popsize){
        Throw(Logic_error("The population size must be the same in the assignment operator"));
        return * this;
    }
    Swarm<ParticleLbest>::operator=(s);
    for(int i=0;i<m_popsize;i++){
        gCopy(mpp_linkInfor[i],s.mpp_linkInfor[i],m_popsize);
    }

    return *this;
}
