#include <iostream>
/*******************begin performance metrics ******************/
#include "../PerformanceMeasure/PopulationInforDOP.h"
#include "../PerformanceMeasure/PerformSingleObj.h"
/*******************end performance metrics ********************/

/*******************begin DOPs ******************/
#include "../Problems/DOPs/MovingPeak.h"
#include "../Problems/DOPs/DF1.h"
#include "../Problems/DOPs/CompositionDBG.h"
#include "../Problems/DOPs/RotationDBG.h"
#include "../Problems/DOPs/BinaryDOP.h"
/*******************end DOPs ********************/

/*******************begin FOPs ******************/
#include "../Problems/FunctionOpt/Sphere.h"
#include "../Problems/FunctionOpt/FAckley.h"
#include "../Problems/FunctionOpt/FGriewank.h"
#include "../Problems/FunctionOpt/FRastrigin.h"
#include "../Problems/FunctionOpt/FWeierstrass.h"
#include "../Problems/FunctionOpt/HybridComp.h"
#include "../Problems/FunctionOpt/FRosenbrock.h"
#include "../Problems/FunctionOpt/FElliptic.h"
#include "../Problems/FunctionOpt/FMAX_global1.h"
#include "../Problems/FunctionOpt/FMAX_global2.h"
#include "../Problems/FunctionOpt/FMAX_global3.h"
#include "../Problems/FunctionOpt/FMAX_global4.h"
#include "../Problems/FunctionOpt/FMAX_global5.h"
#include "../Problems/FunctionOpt/FNoncont_Rastrigin.h"
#include "../Problems/FunctionOpt/FPenalized_1.h"
#include "../Problems/FunctionOpt/FPenalized_2.h"
#include "../Problems/FunctionOpt/FQuartic_Noisy.h"
#include "../Problems/FunctionOpt/FScaffer_F6.h"
#include "../Problems/FunctionOpt/FSchwefel.h"
#include "../Problems/FunctionOpt/FSchwefel_1_2.h"
#include "../Problems/FunctionOpt/FSchwefel_2_22.h"
#include "../Problems/FunctionOpt/FStep.h"
#include "../Problems/FunctionOpt/Schwefel_2_21.h"
#include "../Problems/RealWorld/FGear_Train.h"
#include "../Problems/RealWorld/FParEst_FMSoundWaves.h"
/*******************end FOPs ********************/

/*******************begin algorithms ******************/
#include "../Algorithms/Chromosome.h"
#include "../Algorithms/PSO/StandardGBest/SPSOGBest.h"
#include "../Algorithms/PSO/StandardLBest/SwarmLBest.h"
#include "../Algorithms/PSO/SLPSO/SLSwarm.h"
#include "../Algorithms/PSO/ClusteringPSO/ClusteringPSO.h"
#include "../Algorithms/PSO/ClusteringPSO/ClusteringPSOReady.h"
#include "../Algorithms/DE/DEIndividual.h"
#include "../Algorithms/DE/DEPopulation.h"
#include "../Algorithms/DE/CRDE/CRDE.h"
#include "../Algorithms/HybridAlgs/ESCA/ESCA.h"
#include "../Algorithms/GA/SGAReal.h"
/*******************end algorithms ********************/

/*******************begin system initialization  ******************/
#include "parameters.h"
#include "test.h"
/*******************end system initialization ********************/


using namespace std;
extern BasicFunc gFunctionInstance;

void exitSystem(){

    for(unsigned int i=0;i<gAlgorithm.size();i++){
        delete [] gAlgPro[i];
    }
    delete []gAlgPro;

    gProblem.clear();
    gAlgorithm.clear();

    gFunctionInstance.clear();

    gDeleteRandPro();
    gDeleteRandAlg();

}


void go(char *rPar[], int num){

    double gOpt;

    for( Global::g_runIdx=0;Global::g_runIdx<Global::g_numRuns;Global::g_runIdx++) {

        switch(Global::g_algNumber){
            case ALG_SPSO:
                Global::gp_algorithm=new SPSOGBest(Global::g_gPopsize);
                break;
            case ALG_SPSO07:
                Global::gp_algorithm=new SwarmLBest(Global::g_gPopsize);
                dynamic_cast<SwarmLBest *>(Global::gp_algorithm)->initializePara(1./(2.*log(2.)),0.5 + log( 2. ),0.5 + log( 2. ));
                break;
            case ALG_SLPSO:
                Global::gp_algorithm=new SLSwarm(Global::g_gPopsize);
               dynamic_cast<SLSwarm *>(Global::gp_algorithm)->initializePara(0.89,1.496,1.496,true,0.89,0.4);
               break;
            case ALG_CPSO:
                Global::gp_algorithm=new ClusteringMethod<Swarm<Particle>, Particle>();
                Global::gp_algorithm->setID(ALG_CPSO);
                Global::g_sigma=0.0001;
                break;
            case ALG_CPSOR:
                Global::gp_algorithm=new ClusteringReadyMethod<Swarm<Particle>, Particle>();
                Global::gp_algorithm->setID(ALG_CPSOR);
                Global::g_sigma=0.0001;
                break;
			case ALG_CDER:
                Global::gp_algorithm=new ClusteringReadyMethod<DEPopulation<DEIndividual>, DEIndividual>();
                Global::gp_algorithm->setID(ALG_CDER);
                Global::g_sigma=0.01;
                break;
            case ALG_CRDE:
                Global::gp_algorithm=new CRDE(Global::g_gPopsize);
				Global::gp_algorithm->setID(ALG_CRDE);
                break;
			case ALG_ESCA:
                Global::gp_algorithm=new ESCA(Global::g_gPopsize);
				Global::gp_algorithm->setID(ALG_ESCA);
                break;
            case ALG_SGA_REAL:
                Global::gp_algorithm=new SGAReal(Global::g_gPopsize);
				Global::gp_algorithm->setID(ALG_SGA_REAL);
                break;

        }

        switch(Global::g_proNumber){
            case Sphere: case Sphere_Noisy: case S_Sphere: case R_Sphere: case RS_Sphere: case S_Sphere_CEC05:
            case Rastrigin: case Rastrigin_Noisy: case S_Rastrigin: case R_Rastrigin: case RS_Rastrigin:
            case S_Rastrigin_CEC05: case RS_Rastrigin_CEC05: case Weierstrass: case RS_Weierstrass: case R_Weierstrass:
            case RS_Weierstrass_CEC05: case Griewank: case R_Griewank: case RS_Griewank: case Ackley: case Ackley_Noisy:
            case S_Ackley: case R_Ackley: case RS_Ackley: case RS_Ackley_Bound_CEC05: case Step: case Quartic_Noisy:
            case Scaffer_F6: case Rosenbrock: case S_Rosenbrock: case S_Rosenbrock_CEC05: case Schwefel_2_22:
            case  Schwefel_2_22_Noisy: case S_Schwefel_2_22: case R_Schwefel_2_22: case RS_Schwefel_2_22: case Schwefel:
            case Schwefel_Noisy: case S_Schwefel: case R_Schwefel: case RS_Schwefel: case Schwefel_1_2: case Schwefel_1_2_Noisy:
            case S_Schwefel_1_2: case S_Schwefel_1_2_Noisy: case R_Schwefel_1_2: case RS_Schwefel_1_2: case  S_Schwefel_1_2_CEC05:
            case  S_Schwefel_1_2_Noisy_CEC05: case Schwefel_2_21: case Penalized_1: case Penalized_2: case Noncont_Rastrigin:
            case RS_Elliptic_CEC05: case Elliptic: case Com: case R_Com: case Com_CEC05: case H_Com_CEC05: case H_Com_Noisy_CEC05:
            case RH_Com_CEC05: case RH_Com_NarrowBasin_CEC05: case RH_Com_Bound_CEC05: case M_global1: case M_global2:
            case M_global3: case M_global4: case M_global5: case S_Sphere_CEC08: case Schwefel_2_21_CEC08: case S_Rosenbrock_CEC08:
            case S_Rastrigin_CEC08: case S_Griewank_CEC08: case S_Ackley_CEC08: case RW_Gear_Train: case RW_ParEst_FMSoundWaves:
                if(dynamic_cast< BenchmarkFunction *>(Global::gp_problem)->getKnownFlagGOpt()) {
                    Global::gp_problem->getObjGlobalOpt(&gOpt);
                   PerformSingleObj::getPerformSingleObj()->addGOpt(gOpt);
                }
                break;
            ///Dynamic Problems
            case CompositionDBG_DOP:
            case RotationDBG_DOP: case MovingPeak_DOP: case DF1_DOP:
                Global::gp_problem->getObjGlobalOpt(&gOpt);
                PerformSingleObj::getPerformSingleObj()->addGOpt(gOpt);
                break;
            case Binary_DOP:
                break;
        }
         if(Global::g_runIdx==0){
          // only assign once with the algorithm's parameter
           PerformSingleObj::getPerformSingleObj()->setAlgParameter(Global::gp_algorithm->m_algPar);
         }
        Global::gp_algorithm->run();

        gDeleteRandPro();
        gDeleteRandAlg();

        /// to make sure that the problem uses the same serial random numbers in all runs
        gCreateRandPro(0.5);
        /// set a different seed for each run
        gCreateRandAlg((Global::g_runIdx+1)/(1.0+Global::g_numRuns));

        Global::gp_problem->reset();
        PerformSingleObj::getPerformSingleObj()->resetGOptIndx();
        Global::gp_problem->resetEvaluations();
        delete Global::gp_algorithm;
    }


}


int main(int argc, char *argv[]){
/// /////////////*******************WARNING**********************************************************//////
///             Steps of the implementation of a specific algorithm                                  //////
/// 1. Initialize randome number systems(note, algorithms and problems use two different random number/////
///    generation systems to make sure all algorithms use the exact same problem settings for fair  //////
///    comparison.                                                                                  //////
/// 2. Initialize the problem to be solved.                                                         //////
/// 3. Initialize the algorithm performance measure system.                                         //////
/// 4. Initialize the algorithm to be run															//////
/// 5. Start runing the algorithm.                                                                  //////
/// 6. Calculate the performance and output the results                                             //////
/// /////////////**********************************************************************************///////
    initializeSystem();
    if(initializeProblem(argv,argc)){
        go(argv,argc);
        outPutResult();
    }

    exitSystem();

    return 0;
}
