/*************************************************************************
* 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: 20 July 2011
// Last modified:
#include "BenchmarkFunction.h"
//#include "../../Algorithms/DE/Ming_DE/Ming_Global.h"

BenchmarkFunction::BenchmarkFunction(){
    //ctor
    mp_translation=0;
    mp_rotationMatrix=0;
}

BenchmarkFunction::~BenchmarkFunction(){
    freeMemory();

}
BenchmarkFunction::BenchmarkFunction(const int rId, const int rDimNumber, char *rName):Problem(rId,rDimNumber,C_DECIMAL,CAT_STATIC,rName),
m_scaleFlag(false),m_rotationFlag(false),m_translationFlag(false),m_biasFlag(false),m_scale(1.0),m_bias(0.),m_accuracy(1.0e-6){
    allocateMemory(m_dimNumber);
    zeroTranslation();
    mp_rotationMatrix->Identity();
    m_problemType=MIN_OPT;
    m_globalOpt.m_knownFlag=false;
}

void BenchmarkFunction::allocateMemory(const int rDimNumber ){
    mp_translation=new double[rDimNumber];
    mp_rotationMatrix =new Matrix(rDimNumber,rDimNumber);
    m_globalOpt.mp_location=new double[rDimNumber];
    m_originalGlobalOpt.mp_location=new double[rDimNumber];

    for(int i=0;i<rDimNumber;i++)  {m_searchRange[i]=new Boundary<double>(); }
}

void BenchmarkFunction::freeMemory(){
    if(mp_translation) delete [] mp_translation;

    if(mp_rotationMatrix)       delete mp_rotationMatrix;

    delete [] m_globalOpt.mp_location;
    delete [] m_originalGlobalOpt.mp_location;
    mp_translation=0;
    mp_rotationMatrix=0;
    for(int i=0;i<m_dimNumber;i++) delete  (Boundary<double>*)m_searchRange[i];

}

BenchmarkFunction& BenchmarkFunction::operator=(const BenchmarkFunction & rBF){
    if(this==&rBF) return *this;

    if(m_dimNumber!=rBF.m_dimNumber){
         Throw(Invalid_argument("The number of dimensions must be same!"));
        exit(0);
    }

    Problem::operator=(rBF);

    m_scaleFlag=rBF.m_scaleFlag;
    m_biasFlag=rBF.m_biasFlag;
    m_rotationFlag=rBF.m_rotationFlag;
    m_translationFlag=rBF.m_translationFlag;

    m_scale=rBF.m_scale;
    m_bias=rBF.m_bias;
    m_accuracy=rBF.m_accuracy;

    gCopy(mp_translation,rBF.mp_translation,m_dimNumber);
    *mp_rotationMatrix=*rBF.mp_rotationMatrix;

    m_conditionNumber=rBF.m_conditionNumber;

    m_globalOpt.m_knownFlag=rBF.m_globalOpt.m_knownFlag;
    gCopy(m_globalOpt.mp_location,rBF.m_globalOpt.mp_location,m_dimNumber);
    m_globalOpt.m_value=rBF.m_globalOpt.m_value;

    m_originalGlobalOpt.m_knownFlag=rBF.m_originalGlobalOpt.m_knownFlag;
    gCopy(m_originalGlobalOpt.mp_location,rBF.m_originalGlobalOpt.mp_location,m_dimNumber);
    m_originalGlobalOpt.m_value=rBF.m_originalGlobalOpt.m_value;

    return *this;
}

void BenchmarkFunction::setTranslation(const double *rTrans){
    gCopy(mp_translation,rTrans,m_dimNumber);
    if(isZeroTranslation()) m_translationFlag=false;
    else m_translationFlag=true;

}
void BenchmarkFunction::setRotation(const double * const * rRot){
    mp_rotationMatrix->setData(rRot);

    if(mp_rotationMatrix->isIdentity()) m_rotationFlag=false;
    else m_rotationFlag=true;

}
void BenchmarkFunction::setBias(double rBias){
    m_bias=rBias;
    if(m_bias!=0) m_biasFlag=true;
    else m_biasFlag=false;
}
void BenchmarkFunction::setScale(double rScale){
    m_scale=rScale;

    if(m_scale!=1) m_scaleFlag=true;
    else m_scaleFlag=false;
}
void BenchmarkFunction::setAccuracy(double rAcc){
    m_accuracy=rAcc;
}
void BenchmarkFunction::zeroTranslation(){
     for(int i=0;i<m_dimNumber;i++) mp_translation[i]=0.;

}

bool BenchmarkFunction::isZeroTranslation(){
    for(int i=0;i<m_dimNumber;i++){
        if(mp_translation[i]!=0) return false;
    }

    return true;

}

void BenchmarkFunction::transform(double * x){

    if(!m_translationFlag&&!m_scaleFlag&&!m_rotationFlag) return;


    double *x_=new double[m_dimNumber];
     gCopy(x_,x,m_dimNumber);
    if(m_translationFlag){
        for( int i = 0; i < m_dimNumber; i++ ) 	{
             x_[ i ] = x[ i ] - mp_translation[ i ];
        }
	}

    if(m_scaleFlag){
        for( int i = 0; i < m_dimNumber; i++ )     x_[ i ]/=m_scale;
    }

    if(m_rotationFlag){
        for(int i = 0; i < m_dimNumber; i++ ) {
            x[i] = 0;

		for(int j = 0; j < m_dimNumber; j++ ) {
			x[ i ] += mp_rotationMatrix->vec[ j ].data[ i ] * x_[ j ];
		}
        }
    }
     else gCopy(x,x_,m_dimNumber);

    delete [] x_;
}
void BenchmarkFunction::setConditionNumber(double rC){
    m_conditionNumber=rC;
}

bool BenchmarkFunction::loadTranslation(){
    	// Initial the location of shifted global optimum
     if(!m_originalGlobalOpt.m_knownFlag){
            Throw(Logic_error("error, the original global optimia is not defined!!"));
            exit(0);
        }

	string s;
	char a[100];
	sprintf(a,"%d",m_dimNumber);
	strcat(a,"Dim.txt");
	s=a;
	s.insert(0,gProName[m_id]+"_Opt_");

	s.insert(0,"Problems//FunctionOpt//Data//" );//probDataPath
	ifstream in;
	in.open(s.data());
	if(in.fail()){
		switch(m_id){

		case RS_Ackley_Bound_CEC05:
			for(int j=0;j<m_dimNumber;j++){
				if((j+1)%2==1)
					mp_translation[j]=((Boundary<double> *)m_searchRange[j])->lower;
				else
					mp_translation[j]=((Boundary<double> *)m_searchRange[j])->lower+(((Boundary<double> *)m_searchRange[j])->upper-((Boundary<double> *)m_searchRange[j])->lower)*(1-Global::gp_uniformPro->Next());
			}
			break;

          case RS_Schwefel:
          case S_Schwefel:
			for(int j=0;j<m_dimNumber;j++){
                double rl,ru,range;
                ru=((Boundary<double> *)m_searchRange[j])->upper-m_originalGlobalOpt.mp_location[j];
                rl=m_originalGlobalOpt.mp_location[j]-((Boundary<double> *)m_searchRange[j])->lower;
                range=rl<ru?rl:ru;
                mp_translation[j]=(Global::gp_uniformPro->Next()-0.5)*20;
            }
            break;
          case RS_Griewank_noBounds_CEC05:
			for(int j=0;j<m_dimNumber;j++){
                mp_translation[j] = -600.0 + Global::gp_uniformPro->Next() * 600;
            }
            break;
		default:
            for(int j=0;j<m_dimNumber;j++){
                double rl,ru,range;
                ru=((Boundary<double> *)m_searchRange[j])->upper-m_originalGlobalOpt.mp_location[j];
                rl=m_originalGlobalOpt.mp_location[j]-((Boundary<double> *)m_searchRange[j])->lower;
                range=rl<ru?rl:ru;
                mp_translation[j]=(Global::gp_uniformPro->Next()-0.5)*2*range;
            }

			break;
		}

		ofstream out(s.c_str());
        for(int j=0;j<m_dimNumber;j++)        out<<mp_translation[j]<<" ";

        //out.write((const char*) &(mp_translation[j]), sizeof(mp_translation[j]))<<" ";

		out.close();


	}else{

        for(int j=0;j<m_dimNumber;j++) {
            in>>mp_translation[j];

		}
	}
	in.close();
	m_translationFlag=true;
    return true;

}
bool BenchmarkFunction::loadRotation(){
	string s;
	char a[100];
	sprintf(a,"%d",m_dimNumber);
	strcat(a,"Dim.txt");
	s=a;
	s.insert(0,gProName[m_id]+"_RotM_");

	s.insert(0,"Problems//FunctionOpt//Data//" );//probDataPath
	ifstream in;
	in.open(s.data());
	if(in.fail()){
        switch(m_id){
            case RS_Elliptic_CEC05:
                mp_rotationMatrix->Identity();
                break;
            case RS_Griewank: case RS_Griewank_noBounds_CEC05:
                mp_rotationMatrix->Randomize(false);
                mp_rotationMatrix->GenerateRotationMatrix(m_conditionNumber,false);
                (*mp_rotationMatrix)*(1+0.3*fabs(Global::gp_normalPro->Next()));
                break;
            default:
                mp_rotationMatrix->Randomize(false);
                mp_rotationMatrix->GenerateRotationMatrix(m_conditionNumber,false);
                break;
        }

		ofstream out(s.c_str());
		mp_rotationMatrix->Print(out);
		out.close();
	}else{
		mp_rotationMatrix->Read_Data(in);
	}
	in.close();

    m_rotationFlag=true;
    return true;

}
double * BenchmarkFunction::getTranslation(){
    return mp_translation;
}
Matrix * BenchmarkFunction::getRotation(){
    return mp_rotationMatrix;
}
int BenchmarkFunction::getConditionNumber(){
    return m_conditionNumber;
}

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

    double res;
	bool inbound=true;

    if(m_id==(int)Global::g_proNumber){
        for( int i = 0; i < m_dimNumber; i++ ) {
             if(((Boundary<double> *)m_searchRange[i])->m_flag == false) { inbound=true; break;}
            if( ( x[ i ] < ((Boundary<double> *)m_searchRange[i])->lower ) || ( x[ i ] > ((Boundary<double> *)m_searchRange[i])->upper ) ) {
                inbound = false;
                break;
            }
        }
    }

	if (!inbound){
		res = (m_problemType == MIN_OPT) ? LONG_MAX : LONG_MIN;
	}else
	{
		double *x_=new double[m_dimNumber];
		gCopy(x_,x,m_dimNumber);
		transform(x_);
		res = evaluate_(x_);
		delete [] x_;
	}

    if(rFlag){
        if(m_evals==0) mp_bestSoFar[0]=res;
		m_evals++;

		if(m_problemType == MIN_OPT){
			if(res<mp_bestSoFar[0])mp_bestSoFar[0]=res;
		}else{
			if(res>mp_bestSoFar[0])mp_bestSoFar[0]=res;
		}
        ///TO DO store res to performance measure

    }
	return res;
}

void BenchmarkFunction::setRotationFlag(bool rFlag){
        m_rotationFlag=rFlag;
}
void BenchmarkFunction::setTranlationFlag(bool rFlag){
    m_translationFlag=rFlag;
}
void BenchmarkFunction::setOriginalGlobalOpt(){
    m_originalGlobalOpt.m_knownFlag=true;
    for(int i=0;i<m_dimNumber;i++) m_originalGlobalOpt.mp_location[i]=0;
    m_originalGlobalOpt.m_value=BenchmarkFunction::evaluate(m_originalGlobalOpt.mp_location,false);
}
void BenchmarkFunction::setGlobalOpt(){
    m_globalOpt.m_knownFlag=true;
    for(int i=0;i<m_dimNumber;i++) m_globalOpt.mp_location[i]=0;
    m_globalOpt.m_value=BenchmarkFunction::evaluate(m_globalOpt.mp_location,false);
}
bool BenchmarkFunction::getObjGlobalOpt(double *rOpt, int rNumObj){
    if(m_globalOpt.m_knownFlag){
        *rOpt=m_globalOpt.m_value;
        return true;
    }
    else{
    return false;
    }
}
double BenchmarkFunction::getAccuracy(){
    return m_accuracy;
}
bool BenchmarkFunction::getKnownFlagGOpt(){
    return m_globalOpt.m_knownFlag;
}
