/*************************************************************************
* 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 July 2011
// Last modified:
#include "BinaryDOP.h"


BinaryDOP *BinaryDOP::msp_binaryDOP=0;

BinaryDOP::BinaryDOP(const int rId, const int rDimNumber,const int rNumPeaks, const int rLength,const ChangeType rT,const Encoding rEncoding):DynamicProblem(rId,rDimNumber,rEncoding,rNumPeaks),m_length(rLength){

    DynamicProblem::msc_NumChangeTypies=6;

	allocateMemory(m_numPeaks,m_length);
	calculateInitialAngle();
	m_alpha=0.1;
	m_maxAlpha=0.4;

	m_heightSeverity=0.5;
	m_noisySeverity=0.1;

	for(int i=0;i<m_numPeaks;i++){
	    for(int j=0;j<m_length;j++) {
	    if(Global::gp_uniformPro->Next()<m_alpha) mpp_peak[i][j]=true;
	    else  mpp_peak[i][j]=false;
	    }

	    //mp_height[i]=Global::gp_uniformPro->Next();
	     mp_height[i]=1.0;
	}
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);
    m_problemType=MAX_OPT;

     for(int i=0;i<m_dimNumber;i++)  ((Boundary<bool>*)m_searchRange[i])->m_coding=C_BINARY;

    calculateGlobalOptima();


}
void BinaryDOP::reset(){
	for(int i=0;i<m_numPeaks;i++){
	    for(int j=0;j<m_length;j++) {
	    if(Global::gp_uniformPro->Next()<m_alpha) mpp_peak[i][j]=true;
	    else  mpp_peak[i][j]=false;
	    }

	    //mp_height[i]=Global::gp_uniformPro->Next();
	     mp_height[i]=1.0;
	}
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);
    m_changeCounter=0;
    calculateGlobalOptima();
}
BinaryDOP::~BinaryDOP(){
	freeMemory();
	BinaryDOP::msp_binaryDOP=0;
}

void BinaryDOP::allocateMemory(int rPeaks, int rLength){
   mpp_peak=new bool*[rPeaks];
   mpp_prePeak=new bool*[rPeaks];
   for(int i=0;i<rPeaks;i++){
       mpp_peak[i]=new bool[rLength];
       mpp_prePeak[i]=new bool[rLength];
   }

    mp_height=new double[rPeaks];
    mp_preHeight=new double[rPeaks];
    mp_initialAngle=new double[rPeaks];
    mp_globalOptimaIdx=new bool[rPeaks];
    for(int i=0;i<m_dimNumber;i++)  m_searchRange[i]=new Boundary<bool>();


}
void  BinaryDOP::freeMemory(){
    for(int i=0;i<m_numPeaks;i++){
        delete [] mpp_peak[i];
        delete [] mpp_prePeak[i];
    }

    delete [] mpp_peak;
    delete [] mpp_prePeak;
    delete [] mp_height;
    delete [] mp_preHeight;
    delete [] mp_initialAngle;
    delete [] mp_globalOptimaIdx;
    for(int i=0;i<m_dimNumber;i++) delete  (Boundary<bool>*)m_searchRange[i];

}
double BinaryDOP::evaluate(const bool  *x, bool rFlag){



	int maxDis=0;
	for(int i=0;i<m_numPeaks;i++){
	    double dis=mp_height[i]*(m_length-HammingDistance(const_cast<bool*>(x), mpp_peak[i]));
        if(dis>maxDis) maxDis=dis;
	}
	double obj=maxDis/m_length;

	if(rFlag&&m_evals%m_changeFre==0) mp_bestSoFar[0]=obj;
	if(rFlag&&mp_bestSoFar[0]<obj) mp_bestSoFar[0]=obj;

	if(rFlag)    m_evals++;

    if(rFlag&&m_evals%m_changeFre==0&&m_evals<Global::g_tEvals) DynamicProblem::change();

	return obj;
}
void BinaryDOP::calculateInitialAngle(){

	if(getChangeType()==CT_Recurrent||getChangeType()==CT_RecurrentNoisy){

		// calculate initial phase for recurrent or recurrent_noisy change
		for(int i=0;i<m_numPeaks;i++){
		    int num_ones=0;
            for(int j=0;j<m_length;j++) if(mpp_peak[i][j]==1) num_ones++;
            mp_initialAngle[i]=asin((double)num_ones/(double)m_length)*getPeriod()/(2.*PI);
		}
	}

}

void BinaryDOP::randomChange(){
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);


	for(int i=0;i<m_numPeaks;i++){
	    for(int j=0;j<m_length;j++)
		if(Global::gp_uniformPro->Next()<m_alpha) mpp_peak[i][j]=!mpp_peak[i][j];
	}

	for(int i=0;i<m_numPeaks;i++){
	    double step=(Global::gp_uniformPro->Next()-0.5)*m_heightSeverity;
	    if((mp_height[i]+step)>0&&(mp_height[i]+step)<1)   mp_height[i]= mp_height[i]+step;
	}
	calculateGlobalOptima();
	m_changeType.counter++;

}
void BinaryDOP::recurrentChange(){
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);

	int num_ones;
	for(int i=0;i<m_numPeaks;i++){
        num_ones=(int)(0.5+m_length*(sin(2*PI*(m_changeType.counter+mp_initialAngle[i])/getPeriod())+1)/2.);
        standardChange(num_ones,0,i);
         mp_height[i]=(sin(2*PI*(m_changeType.counter+mp_initialAngle[i])/getPeriod())+1)/2.;
	}
	calculateGlobalOptima();
	m_changeType.counter++;

}
void BinaryDOP::recurrentNoisyChange(){
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);

	int num_ones,t;

	for(int i=0;i<m_numPeaks;i++){
        num_ones=(int)(0.5+m_length*(sin(2*PI*(m_changeType.counter+mp_initialAngle[i])/getPeriod())+1)/2.);
        t=num_ones;
        int noisy=(int)(Global::gp_uniformPro->Next()*m_noisySeverity*m_length);
        standardChange(num_ones,noisy,i);

        mp_height[i]=(sin(2*PI*(m_changeType.counter+mp_initialAngle[i])/getPeriod())+1)/2.+Global::gp_uniformPro->Next()*m_noisySeverity;

	}
	calculateGlobalOptima();
	m_changeType.counter++;

}
void BinaryDOP::chaoticChange(){
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);

    double chaotic_value;
    for(int i=0;i<m_numPeaks;i++){
        chaotic_value=0;
        for(int j=0;j<m_length;j++) if(mpp_peak[i][j]==true) chaotic_value+=1.0;
        chaotic_value/=m_length;

        chaotic_value=m_chaoticConstant*chaotic_value*(1-chaotic_value);
        int num_ones;
        num_ones=(int)(m_length*chaotic_value);
        standardChange(num_ones,0,i);

        mp_height[i]=m_chaoticConstant* mp_height[i]*(1- mp_height[i]);

    }


	m_changeType.counter++;
	calculateGlobalOptima();
}
void BinaryDOP::smallStepChange(){
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);

    for(int i=0;i<m_numPeaks;i++){
        int step;
         step=(int)(m_alpha*Global::gp_uniformPro->Next()*m_length);
        if(step<1) step=1;

        standardChange(step,0,i);
        mp_height[i]=mp_height[i]+(m_alpha*Global::gp_uniformPro->Next()*m_heightSeverity);
    }
    calculateGlobalOptima();
	m_changeType.counter++;
}

void BinaryDOP::largeStepChange(){
    for(int i=0;i<m_numPeaks;i++){
	    gCopy(mpp_prePeak[i],mpp_peak[i],m_length);
    }
    gCopy(mp_preHeight,mp_height,m_numPeaks);

   for(int i=0;i<m_numPeaks;i++){
        int step;
         step=(int)(m_alpha+(m_maxAlpha-m_alpha)*Global::gp_uniformPro->Next()*m_length);
        if(step<1) step=1;

        standardChange(step,0,i);
        mp_height[i]=mp_height[i]+(m_alpha+(m_maxAlpha-m_alpha)*Global::gp_uniformPro->Next()*m_heightSeverity);
    }
    calculateGlobalOptima();
	m_changeType.counter++;
}
void BinaryDOP::standardChange(int num_ones,int step, int rPeakIndx){

    if(step>0){
        int t=num_ones;
        if(Global::gp_uniformPro->Next()<0.5){
            num_ones-=step;
        }else{
            num_ones+=step;
        }
        if(num_ones<0) num_ones=(t+step)%m_length;
        if(num_ones>m_length) num_ones=(t-step)%m_length;
    }

	int *ones=new int[m_length];
	gInitializeRandomArray(ones,m_length,false);

	for(int i=0;i<num_ones;i++){
		mpp_peak[rPeakIndx][ones[i]]=!mpp_peak[rPeakIndx][ones[i]];
	}
	delete []ones;
}


int BinaryDOP::HammingDistance(bool *source, bool * destination){
    int counter=0;
    for(int i=0;i<m_length;i++){
        if(source[i]!=destination[i]) counter++;
    }

    return counter;
}
void BinaryDOP::initialize( int rPeaks,int rLength, const ChangeType rT){
    if(BinaryDOP::msp_binaryDOP){
        Throw(Logic_error("the Binary DOP has been already initialized"));
        return;
    }

    BinaryDOP::msp_binaryDOP=new BinaryDOP(Binary_DOP,1,rPeaks, rLength, rT,C_BINARY);

}
bool BinaryDOP::isInitialized(){
    if(BinaryDOP::msp_binaryDOP) return true;
    else return false;

}
BinaryDOP * BinaryDOP::getBinaryDOP(){
    if(!BinaryDOP::msp_binaryDOP){
        Throw(Logic_error("the Binary DOP is not initialized"));
        return NULL;
    }

    return BinaryDOP::msp_binaryDOP;
}
void BinaryDOP::deleteBinaryDOP(){
    if(BinaryDOP::msp_binaryDOP){
        delete BinaryDOP::msp_binaryDOP;
        BinaryDOP::msp_binaryDOP=0;
    }
}

void BinaryDOP::calculateGlobalOptima(){

    m_globalOptima=gExtremum(mp_height,m_numPeaks,m_problemType);
    m_maxPeaksNumber=0;
	  for(int i=0;i<m_numPeaks;i++){
	      mp_globalOptimaIdx[i]=false;
		 if(mp_height[i]==m_globalOptima){
		     m_maxPeaksNumber++;
		     mp_globalOptimaIdx[i]=true;
		 }
	  }
}
double BinaryDOP::getGlobalMax()const{
    return m_globalOptima;
}
bool BinaryDOP::getObjGlobalOpt(double *rOpt, int rNumObj){
    *rOpt=m_globalOptima;
    return true;
}
void BinaryDOP::printPeak( const int rIdx){
    cout<<"the "<<rIdx<<"th peak, height: "<<mp_height[rIdx]<<" position:"<<endl;
    for(int i=0;i<m_length;i++){
        cout<<mpp_peak[rIdx][i];
    }
    cout<<endl;
}
const bool * const BinaryDOP::getPeak(const int p) const{
    return mpp_peak[p];
}
const bool * const* const BinaryDOP::getAllPeaks()const{
    return mpp_peak;
}
double BinaryDOP::getPeakHeight(const int p)const{
    return mp_height[p];
}
double BinaryDOP::getPrePeakHeight(const int p)const{
    return mp_preHeight[p];
}
const double *const BinaryDOP::getHeight() const{
    return mp_height;
}

const bool *const BinaryDOP::getPrePeak(const int p)const{
    return mpp_prePeak[p];
}
const bool *const BinaryDOP::getGlobalOptimaIdx()const{
    return mp_globalOptimaIdx;
}
int BinaryDOP::getNumberofGlobalOptPeak()const{
    return m_maxPeaksNumber;
}
BinaryDOP &BinaryDOP::operator=(const BinaryDOP &rBDOP){

    if(this== &rBDOP) return * this;

    if(m_numPeaks!=rBDOP.m_numPeaks||m_length!=rBDOP.m_length) return *this;

    DynamicProblem::operator=(rBDOP);


    for(int i=0;i<m_numPeaks;i++){
        gCopy(mpp_peak[i],rBDOP.mpp_peak[i],m_length);
        gCopy(mpp_prePeak[i],rBDOP.mpp_prePeak[i],m_length);
    }
    gCopy(mp_height,rBDOP.mp_height,m_numPeaks);

    gCopy(mp_preHeight,rBDOP.mp_preHeight,m_numPeaks);

    m_heightSeverity=rBDOP.m_heightSeverity;
    m_globalOptima=rBDOP.m_globalOptima;
    gCopy(mp_globalOptimaIdx,rBDOP.mp_globalOptimaIdx,m_numPeaks);
    m_maxPeaksNumber=rBDOP.m_maxPeaksNumber;

    return *this;

}

void BinaryDOP::setHeight(const double *h){
    gCopy(mp_height,h,m_numPeaks);
}
void BinaryDOP::setPosition(const bool * const * const p){
    for(int i=0;i<m_numPeaks;i++){
        gCopy(mpp_peak[i],p[i],m_length);
    }
}
int BinaryDOP::getLength(){
    return m_length;
}
