/*************************************************************************
* 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 July 2011
// Last modified:
#ifndef SWARM_H
#define SWARM_H
#include "../Population.h"
#include "../Optima.h"
#include "../../Problems/DOPs/DynamicProblem.h"
template <class T>
class Swarm: public Population<T>{
public:
	double m_C1,m_C2;                       //accelerators
	double m_W,m_maxW, m_minW;                // inertia weight
	int m_pBestIdx;

public:
	virtual ~Swarm(void){}
	virtual void initialize( bool mode=true);

	Swarm(void);
	Swarm(const int rPopsize,bool mode=true);
	Swarm(Swarm<T> &s);
	Swarm(Group<T> &g);
	Swarm(Chromosome & center, double radius,const int rPopsize,bool mode=true);

	const int findPBest();

	virtual Swarm &operator= ( Swarm &s);
	virtual int evolve();            // return 2 if dimensional change happens, 1 for non-dimensonal change, 0 for no change
	void initializePara(double rw,double rc1,double rc2,bool write2File=true,double rmaxw=0,double rminw=0);
	void setW(const double rw);
	void setC1(const double rc1);
	void setC2(const double rc2);
	int updateBest(const int p,double ratio);                  // return 2 if dimensional change happens, 1 for non-dimensonal change, 0 for no change

	virtual void reInitialize();
	void reInitialize(Chromosome & center, bool mode=true);
	double getAvgVelocity();
};


template<class T>
Swarm<T>::Swarm(void):Population<T>(){

}

template<class T>
Swarm<T>::Swarm(const int rPopsize, bool mode):Population<T>(rPopsize,mode){
	// for static optimization problems
    initialize();

}
template<class T>
Swarm<T>::Swarm(Swarm &s):Population<T>(s){
	m_W=s.m_W;
	m_C1=s.m_C1;
	m_C2=s.m_C2;
	m_maxW=s.m_maxW;
	m_minW=s.m_minW;
}
template<class T>
Swarm<T>::Swarm(Group<T> &g):Population<T>(g){

	initialize();
}
template<class T>
Swarm<T>::Swarm(Chromosome & center, double radius,const int rPopsize,bool mode):Population<T>(center,radius,rPopsize,mode){
	initialize();
}
template<class T>
void Swarm<T>::initialize(bool mode){

    // for static optimization problems
	if(Global::gp_problem->getProblemCat()==CAT_STATIC)
		initializePara(0.7298f,1.496f,1.496f);
	else // for dynamic problems
		initializePara(0.6f,1.7f,1.7f,0.6f,0.3f);
}
template<class T>
Swarm<T> &Swarm<T>::operator= (Swarm &s){
	if(this==&s) return *this;
	Population<T>::operator=(s);

	m_W=s.m_W;
	m_C1=s.m_C1;
	m_C2=s.m_C2;
	m_maxW=s.m_maxW;
	m_minW=s.m_minW;
	return *this;
}

template<class T>
const int Swarm<T>::findPBest(){

	if(this->m_popsize<1){
		m_pBestIdx=-1;
		return m_pBestIdx;
	}
	m_pBestIdx=0;
	for(int i=1;i<this->m_popsize;i++){
		if(this->mp_pop[i].m_pbest>this->mp_pop[m_pBestIdx].m_pbest){
			m_pBestIdx=i;
		}
	}

    if(m_pBestIdx!=0||this->m_popsize==1){
        return m_pBestIdx;
    }
    else if(!(this->mp_pop[m_pBestIdx]>=this->mp_pop[this->m_popsize-1])){
        m_pBestIdx=-1;

    }
    return m_pBestIdx;

}




template<class T>
int Swarm<T>::evolve(){
	if(this->m_popsize<1) return 0;
	Chromosome t;
	int r_flag=-1;
	for(int i=0;i<this->m_popsize;i++){
		t=this->mp_pop[i].m_pself;
		if(Global::g_algNumber==ALG_CPSORL){
		    //using lbest mode with ring topology
            int pre,next,lbest;
            pre=(i-1+this->m_popsize)%this->m_popsize;
            next=(i+1)%this->m_popsize;
            lbest=this->mp_pop[pre].m_pbest>this->mp_pop[next].m_pbest?pre:next;
            lbest=this->mp_pop[lbest].m_pbest>this->mp_pop[i].m_pbest?lbest:i;
            this->mp_pop[i].moveBound(this->mp_pop[i].m_pbest,this->mp_pop[lbest].m_pbest,m_W,m_C1,m_C2);
		}else{
		    // using gbest mode
            this->mp_pop[i].moveBound(this->mp_pop[i].m_pbest,this->m_best,m_W,m_C1,m_C2);
		}

		if(gIsTerminate()){ r_flag=0; break;}
			

		if(this->mp_pop[i].m_pself>this->mp_pop[i].m_pbest){
			this->mp_pop[i].m_pbest=this->mp_pop[i].m_pself;
			if(this->mp_pop[i].m_pself>this->m_best){
				this->m_best=this->mp_pop[i].m_pself;
			}
		}

		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;}

			this->updateMemoryAll();
			t.evaluate(false);
			r_flag=1; break;
		}

		if(Global::gp_algorithm->getID()==ALG_CPSO||Global::gp_algorithm->getID()==ALG_CPSOR||Global::gp_algorithm->getID()==ALG_CPSOD){//
			int flag=0;
			if(this->mp_pop[i].m_pself>t){
				flag=updateBest(i,1.0);
			}

			if(flag!=0) { r_flag=flag; break;}

			if(gIsTerminate()) { r_flag=0; break;}
				
		}
	}
	if(r_flag==-1){
		int num=(dynamic_cast<DynamicProblem*>(Global::gp_problem)->getChangeFre()-Global::gp_problem->getEvaluations())/Population<T>::ms_gNumIndis;
		this->m_evoNum++;
	   if(Global::gp_algorithm->getID()==ALG_CPSO){
			m_W=m_maxW-(m_maxW-m_minW)*this->m_evoNum/(this->m_evoNum+num);
			if(m_W<m_minW)		m_W=m_minW;
	   }
	}
	   return r_flag;
}

template<class T>
void Swarm<T>::initializePara(double rw,double rc1,double rc2,bool write2File,double rmaxw,double rminw){
    if(write2File){
	setW(rw);
	setC2(rc2);
	setC1(rc1);
    }else{
        m_W=rw; m_C1=rc1;m_C2=rc2;
    }
	m_maxW=rmaxw;
	m_minW=rminw;
}

template<class T>
int Swarm<T>::updateBest(const int p,double ratio){
    Chromosome x;
	int r_flag=-1;
	for( int j=0;j<this->m_dimNumber;j++){
		if(ratio<1&&Global::gp_uniformAlg->Next()>ratio) continue;
        x=this->m_best;
		Chromosome & self= this->mp_pop[p].m_pself;
        x.getGene<double>(j)=self.getGene<double>(j);
        x.evaluate();

        if(x>this->m_best)   this->m_best=x;

		if(gIsTerminate()){r_flag=0;break;}
			

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



template<class T>
void Swarm<T>::reInitialize(){
	for(int i=0;i<Population<T>::m_popsize;i++) Population<T>::mp_pop[i].initialize(i,i+1); // index, ID

	this->findBest();
	this->findWorst();
	if(this->m_bestIdx!=-1&&this->mp_pop[this->m_bestIdx].getRepresentative()>this->m_best) this->m_best=this->mp_pop[this->m_bestIdx].getRepresentative();
}

template<class T>
void Swarm<T>::reInitialize(Chromosome & center, bool mode){
	
	Population<T>::initialize(center,this->m_initialRadius,mode);
	this->computeCenter();
	this->computeInitialRadius();
	this->findBest();
	this->findWorst();
	if(this->m_bestIdx!=-1&&this->mp_pop[this->m_bestIdx].getRepresentative()>this->m_best) this->m_best=this->mp_pop[this->m_bestIdx].getRepresentative();

}


template<class T>
double Swarm<T>::getAvgVelocity(){
	double avg=0;
	if(this->m_popsize<1) return 0;
	for(int i=0;i<this->m_popsize;i++) avg+=this->mp_pop[i].getVelocity();

	return avg/this->m_popsize;

}

template<class T>
void Swarm<T>::setW(const double rw){
	m_W=rw;

	size_t start, end;
    start=this->m_algPar.str().find("Inertia weight:");
    for(size_t i=start;i<this->m_algPar.str().size();i++){
        if(this->m_algPar.str()[i]==';') {
            end=i;
            break;
        }
    }
    stringstream ss;

    ss<<"Inertia weight:"<<m_W<<"; ";
	if(start!=string::npos){
		string result=this->m_algPar.str();
		result.replace(start,end-start+1, ss.str());
		 this->m_algPar.str(result);
	}else this->m_algPar<<ss.str();
}
template<class T>
void Swarm<T>::setC1(const double rc1){
	m_C1=rc1;

	size_t start, end;
    start=this->m_algPar.str().find("Accelerate constant1:");
    for(size_t i=start;i<this->m_algPar.str().size();i++){
        if(this->m_algPar.str()[i]==';') {
            end=i;
            break;
        }
    }
    stringstream ss;
    ss<<"Accelerate constant1:"<<m_C1<<"; ";
	if(start!=string::npos){
		string result=this->m_algPar.str();
		result.replace(start,end-start+1, ss.str());
		 this->m_algPar.str(result);
	}else this->m_algPar<<ss.str();


}
template<class T>
void Swarm<T>::setC2(const double rc2){
	m_C2=rc2;

	size_t start, end;
    start=this->m_algPar.str().find("Accelerate constant2:");
    for(size_t i=start;i<this->m_algPar.str().size();i++){
        if(this->m_algPar.str()[i]==';') {
            end=i;
            break;
        }
    }
    stringstream ss;
    ss<<"Accelerate constant2:"<<m_C2<<"; ";
	if(start!=string::npos){
		string result=this->m_algPar.str();
		result.replace(start,end-start+1, ss.str());
		 this->m_algPar.str(result);
	}else this->m_algPar<<ss.str();

}
#endif // SWARM_H
