/*************************************************************************
* 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: 11 May 2011
// Last modified:

#include "DynamicProblem.h"
#include "../../PerformanceMeasure/PerformSingleObj.h"
int DynamicProblem::msc_NumChangeTypies=8;

#ifdef DEMON_EALIB
#include <windows.h>
#include <process.h>   
extern void calculateSamplePoints();
extern HANDLE g_hthDraw;
double g_avgErrSoFar=0;
#endif

DynamicProblem::DynamicProblem(const int rId, const int rDimNumber, const Encoding rEncoding,const int rNumPeaks):Problem(rId,rDimNumber,rEncoding, CAT_DYNAMIC),m_changeCounter(0)
,m_dimNumberTemp(rDimNumber),m_numPeaks(rNumPeaks),m_numPeaksTemp(rNumPeaks){
    //ctor
    m_changeFre=5000;
    m_changeType.type=CT_Random;
    m_changeType.counter=0;
    m_period=0;
    m_flagDimensionChange=false;
    m_dirDimensionChange=true;
    m_synchronize=true;
    m_noisySeverity=0.8;
    m_maxChangeNumber=100;
    m_alpha=0.04;
    m_maxAlpha=0.1;
    m_chaoticConstant=3.67; //in [1,4]

    m_flagNumPeaksChange=false;
    m_dirNumPeaksChange=true;

    m_proPar<<"Change frequency:"<<m_changeFre<<"; "<<"Peaks:"<<m_numPeaks<<"; "<<"NumPeaksChange:"<<m_dirNumPeaksChange<<"; ";

}

DynamicProblem::~DynamicProblem()
{
    //dtor
}
int DynamicProblem::getNumberofPeak()const{
	return m_numPeaks;
}

void DynamicProblem::setNumPeaksChange(const bool rPC){
		 m_flagNumPeaksChange=rPC;
    size_t start, end;
    start=m_proPar.str().find("NumPeaksChange:");
    for(int i=start;i<m_proPar.str().size();i++){
        if(m_proPar.str()[i]==';') {
            end=i;
            break;
        }
    }
    stringstream ss;
    ss<<"NumPeaksChange:"<<m_dirNumPeaksChange<<"; ";
    string result=m_proPar.str();
    result.replace(start,end-start+1, ss.str());
    m_proPar.str(result);
}

void DynamicProblem::setChangeFre(const int rChangeFre){
    if(rChangeFre>0) m_changeFre=rChangeFre;
    else{
        Throw(Invalid_argument("Change frequncy must be greater than 0"));
        exit(0);
    }

    size_t start, end;
    start=m_proPar.str().find("Change frequency:");
    for(int i=start;i<m_proPar.str().size();i++){
        if(m_proPar.str()[i]==';') {
            end=i;
            break;
        }
    }
    stringstream ss;
    ss<<"Change frequency:"<<m_changeFre<<"; ";
    string result=m_proPar.str();
    result.replace(start,end-start+1, ss.str());
    m_proPar.str(result);


}
bool DynamicProblem::setPeriod(const int rPeriod){
    if(rPeriod>=0) m_period=rPeriod;
    else{
        Throw(Invalid_argument("period must be positive"));
        exit(0);
    }
    return true;
}
void DynamicProblem::setChangeType(const SChangeType &rChangeType){
    m_changeType=rChangeType;
}
void DynamicProblem::setChangeType(const ChangeType rT){
    m_changeType.type=rT;
}

void DynamicProblem::setDimensionChange(const bool rFlag){
    m_flagDimensionChange=rFlag;
}

void DynamicProblem::setChangeDirction(const bool rFlag){
    m_dirDimensionChange=rFlag;
}
void DynamicProblem::setSynchronize(const bool rFlag){
    m_synchronize=rFlag;
}

void DynamicProblem::setNoisySeverity(const float rSeverity){

    m_noisySeverity=rSeverity;
}

void DynamicProblem::setMaxChangeNumber(const int rMaxChangeNum){
    m_maxChangeNumber=rMaxChangeNum;
}

void DynamicProblem::change(){

#ifdef DEMON_EALIB
	double gOpt;
	Global::gp_problem->getObjGlobalOpt(&gOpt);
    g_avgErrSoFar=(g_avgErrSoFar*m_changeCounter+fabs(mp_bestSoFar[0]-gOpt))/(m_changeCounter+1);
#endif
	m_changeCounter++;
	switch(getChangeType()){
	case CT_Random:
		randomChange();
		break;
	case CT_Recurrent:
		recurrentChange();
		break;
	case CT_RecurrentNoisy:
		recurrentNoisyChange();
		break;
	case CT_SmallStep:
		smallStepChange();
		break;
	case CT_LargeStep:
		largeStepChange();
		break;
	case CT_Chaotic:
		chaoticChange();
		break;
	default :
		break;
	}

    if(m_flagDimensionChange){

        Global::g_dimNumber=m_dimNumber;
        if(Global::g_dimNumber==msc_MinDimensionNumber)
        m_dirDimensionChange=true;
        if(Global::g_dimNumber==msc_MaxDimensionNumber)
        m_dirDimensionChange=false;

        if(m_dirDimensionChange==true) {
        m_dimNumberTemp++;
        Global::g_dimNumber++;
        }
        else {
        m_dimNumberTemp--;
         Global::g_dimNumber--;
        }
        changeDimension();
    }

    if(m_flagNumPeaksChange){

        if((unsigned int)m_numPeaks>=msc_MaxNumPeaks-1) m_dirNumPeaksChange=false;

        if((unsigned int)m_numPeaks<=msc_MinNumPeaks+1) m_dirNumPeaksChange=true;

        if(m_dirNumPeaksChange==true) m_numPeaksTemp=m_numPeaks+2;
        else m_numPeaksTemp=m_numPeaks-2;

        changeNumPeaks();
    }

    #ifdef EALIB
		double gOpt;
		if(PerformSingleObj::msp_perf&&Global::gp_problem->getObjGlobalOpt(&gOpt)){
		   PerformSingleObj::getPerformSingleObj()->addGOpt(gOpt);
		}
	#endif

	#ifdef DEMON_EALIB
	calculateSamplePoints();
	#endif
	
}



 DynamicProblem & DynamicProblem::operator=(const DynamicProblem & rDP){
    if(this==&rDP) return *this;

    if(m_dimNumber!=rDP.m_dimNumber){
        Throw(Invalid_argument("The number of dimensions must be same!"));
        exit(0);
    }
    if(m_changeType.type!=rDP.m_changeType.type){
        Throw(Invalid_argument("The change type must be same!"));
        exit(0);
    }
    if(m_numPeaks!=rDP.m_numPeaks){
        Throw(Invalid_argument("The number of peaks must be same!"));
        exit(0);

    }
    Problem::operator=(rDP);


    m_changeType.counter=rDP.m_changeType.counter;
    m_changeFre=rDP.m_changeFre;
    m_period=rDP.m_period;
    m_flagDimensionChange=rDP.m_flagDimensionChange;
    m_dirDimensionChange=rDP.m_dirDimensionChange;
    m_synchronize=rDP.m_synchronize;
    m_noisySeverity=rDP.m_noisySeverity;
    m_maxChangeNumber=rDP.m_maxChangeNumber;

    m_alpha =rDP.m_alpha;
    m_maxAlpha= rDP.m_maxAlpha;
    m_chaoticConstant =rDP.m_chaoticConstant;

    m_flagNumPeaksChange=rDP.m_flagNumPeaksChange;
    m_dirNumPeaksChange=rDP.m_dirNumPeaksChange;

    return *this;
 }

 void  DynamicProblem::parameterSetting(Problem * rdp){

	 Problem::parameterSetting(rdp);

	 DynamicProblem *rDP=dynamic_cast<DynamicProblem*>(rdp);
    m_changeType=rDP->m_changeType;
    m_changeFre=rDP->m_changeFre;
    m_period=rDP->m_period;
    m_flagDimensionChange=rDP->m_flagDimensionChange;
    m_dirDimensionChange=rDP->m_dirDimensionChange;
    m_synchronize=rDP->m_synchronize;
    m_noisySeverity=rDP->m_noisySeverity;
    m_maxChangeNumber=rDP->m_maxChangeNumber;
    m_alpha =rDP->m_alpha;
    m_maxAlpha= rDP->m_maxAlpha;
    m_chaoticConstant =rDP->m_chaoticConstant;

    m_flagNumPeaksChange=rDP->m_flagNumPeaksChange;
    m_dirNumPeaksChange=rDP->m_dirNumPeaksChange;
 }

 double DynamicProblem::sinValueNoisy(const int x,const double min, const double max, const double amplitude, const double angle,const double noisy_severity){
														// return a value in recurrent with noisy dynamism environment
	double y;
	double noisy,t;
	y=min+amplitude*(sin(2*PI*(x+angle)/m_period)+1)/2.;
	noisy=noisy_severity*Global::gp_normalPro->Next();
	t=y+noisy;
	if(t>min&&t<max) y=t;
	else y= t-noisy;
	return y;
}

double DynamicProblem::chaoticStep(const double x, const double min, const double max, const float scale){
	if(min>max) return -1;
	double chaotic_value;
	chaotic_value=(x-min)/(max-min);
	chaotic_value=m_chaoticConstant*chaotic_value*(1-chaotic_value);
	//return fabs((min+chaotic_value*(max-min)-x)* Global::scale);
	return chaotic_value*scale;
}

bool DynamicProblem::predictChange(const int evalsMore){
    int fre=getChangeFre();
    int evals=getEvaluations()%fre;
    if(evals+evalsMore>=fre) return true;
    else return false;
}
