#include <iostream>
#include <cstdlib>
#include <ctime>
#include <numeric>
#include <algorithm>
#include <vector>
#include <random>
#include <chrono>
#include <cmath>
#include <windows.h>

#include <limits>
#include <fstream>
#include <sstream>
#include <stdlib.h>

#include "EA.h"
#include "RLS.h"
#include "analysis.h"

#include "crossover-rate.h"
#include "scripts.h"
#include "scripts2.h"
#include "bestPGA.h"
#include "algo-complexity.h"

using namespace std;

int main(int argc, char** argv) {
    srand(time(NULL));
    std::default_random_engine randomizer{static_cast<long unsigned int>(time(0))};

    if(argc >= 2) {
        int num_experiment = atoi(argv[1]);
        switch(num_experiment) {
            case 1:
                cout << "Experiment: Optimal Crossover probability" << endl;
                switch(atoi(argv[2])) {
                    case 0: // Start from beginning
                        script_crossover_proba_global_optim(randomizer, "global-crossover-proba.csv", false, 5, 5);
                        break;
                    case 1: // Restart from a specific point
                        {
                            stringstream ss;
                            ss << "global-crossover-proba-" << atoi(argv[3]) << "-" << atoi(argv[4]) << ".csv";
                            script_crossover_proba_global_optim(randomizer, ss.str(), true, atoi(argv[3]), atoi(argv[4]));
                        }break;
                    case 2: // Filtering
                        crossover_model_filtering(argv[3], argv[4], argv[5], atoi(argv[6]), atoi(argv[7]));
                        break;
                    case 3: // Local optimization of model
                        crossover_model_optimization(argv[3]);
                        break;
                    case 4: // Validation of model
                        crossover_model_validation(argv[3], atof(argv[4]), atof(argv[5]), atof(argv[6]), atof(argv[7]));
                        break;
                }



                return 0;
        }
    }
    ////////////////////////////////////////////////////

    RLS algo;
    vector<Statistic> stats = compute_complexity(randomizer, &algo, 10, 1000, 10, 100, 1.9842169516, true);
    save_in_file("RLS-complexity", stats);
    return 0;

    //script_optimal_proba(randomizer);

    pga_optimisation(10, 20, randomizer);
    return 0;


    int nb_colors = 50;
    int nb_cases = 50;

    MasterMind mm(nb_colors, nb_cases, randomizer);
    RLS algo1;
    EA algo2(100, 100, PLUS, 1./nb_colors, randomizer);
    GA algo3(100, 100, PLUS, 1./nb_cases, cr(nb_colors, 100), UNIFORM_CROSSOVER, GA_V1, randomizer);
    cout << cr(nb_colors, 100) << endl;
    EA algo4(500, 500, PLUS, 1./nb_colors, randomizer);
    GA algo5(500, 500, PLUS, 1./nb_cases, cr(nb_colors, 500), UNIFORM_CROSSOVER, GA_V1, randomizer);
    cout << cr(nb_colors, 500) << endl;
    GA algo6(100, 100, PLUS, 1./nb_cases, 0.8, UNIFORM_CROSSOVER, GA_V1, randomizer);
    algo1.set_problem(&mm);
    algo2.set_problem(&mm);
    algo3.set_problem(&mm);
    algo4.set_problem(&mm);
    algo5.set_problem(&mm);
    algo6.set_problem(&mm);
    vector<SearchHeuristic*> algos = {&algo1, &algo2, &algo3, &algo4, &algo5, &algo6};

    Statistic stat = avg_calls(&algo1, 20, -1, 3.883);
    int max_nb_calls = ceil(stat.average+stat.alea)*2;


    cmp_evolution(algos, mm, max_nb_calls, 100);
    return 0;
/*
    Statistic* curves[6];
    Statistic* dcurves[6];
    int length = 0;
    for(int i=0; i<algos.size(); i++) {
        curves[i] = avg_evolution(algos[i], max_nb_calls, 20);
        dcurves[i] = derivate(curves[i], max_nb_calls);
        length = max_nb_calls-1;
        for(int k=0; k<7; k++) {
            Statistic* cdcurve = compress(dcurves[i], length);
            delete[] dcurves[i];
            dcurves[i] = cdcurve;
            length = ceil(length/2.)-1;
        }
        for(int k=0; k<15; k++) {
            Statistic* sdcurve = smooth(dcurves[i], length);
            delete[] dcurves[i];
            dcurves[i] = sdcurve;
        }
    }

    ofstream testfile("42.csv");
    for(int i=0; i<length; i++) {
        for(int j=0; j<algos.size(); j++)
            testfile << dcurves[j][i].average << ";";
        testfile << endl;
    }
    ofstream testfile2("43.csv");
    for(int i=0; i<max_nb_calls; i++) {
        for(int j=0; j<algos.size(); j++)
            testfile2 << curves[j][i].average << ";";
        testfile2 << endl;
    }
    for(int j=0; j<algos.size(); j++) {
        delete[] dcurves[j];
        delete[] curves[j];
    }

    return 0;

    int initial_n = 3;
    int final_n = 25;

    /// Mu-Lambda Optimisation
    MuLambdaOptimizer mlo(initial_n, randomizer);
    mlo.set_nb_contexts(final_n - initial_n);
    mlo.set_study_interval(1, 100, 4);
    mlo.add_test_wave(100, 2.4751374205, 0);
    mlo.add_test_wave(1000, 2.4365918443, 1);
    mlo.add_test_wave(10000, 2.4327997145, 2);
    //mlo.run();


/*
    //optimal_mulambda(3, 16, randomizer, 1, 50, 1, 100, 1.9842169516);
    return 0;

    optimal_lambda(20, 1, 2, randomizer, 1, 100, 1, 50, 1.9842169516, true);
    return 0;
*/
/*    int nb_tests = 100;
    MasterMind mm(nb_colors, nb_cases, randomizer);
    RLS algo1;
    GA algo3(10, 10, PLUS, 1./nb_cases, 2./nb_cases, UNIFORM_CROSSOVER, GA_V1, randomizer);

    algo1.set_problem(&mm);
    Statistic stat = avg_calls(&algo1, 20, -1, 3.883);
    int max_nb_calls = ceil(stat.average);

    cmp_evolution({&algo1, &algo3}, mm, max_nb_calls, nb_tests);
    return 0;
*/
    //cout << "=========================" << endl;
    //optimal_proba(5,15, randomizer);
    //optimal_proba_v2(5,15, randomizer);
    //optimal_proba_vmath2_rec(3, 16, randomizer, 0.05, 0.5, 0.01, {20, 200, 2000}, {2.3362421597,2.3451370823,3.2953981367});
    //optimal_proba_vmath2_rec(16, 25, randomizer, 0.02, 0.2, 0.005, {20, 200, 2000}, {2.3362421597,2.3451370823,3.2953981367});

    cout << "FINISHED !" << endl;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();

    return 0;

    cout << "=========================" << endl;
    for(int n=5; n<6; n++) {
        vector<double> avg_list;
        vector<double> lambdas;

        int lambda_init = 1;
        int lambda_final = 20;
        int nb_tests = 10000;

        auto start = start_chrono();
        for(int lambda=lambda_init; lambda<lambda_final; lambda++) {
            EA algo(1, lambda, PLUS, 1./n, randomizer);
            MasterMind mm(n, n, randomizer);
            algo.set_problem(&mm);
            Statistic stat = avg_calls(&algo, nb_tests);
            double avg = stat.average;
            avg_list.push_back(avg);
            lambdas.push_back(lambda);
            cout << " -> lambda=" << lambda << ", nbcalls=" << avg << endl;
        }
        auto stop = stop_chrono();
        int elapsed_milliseconds = interval_chrono(start, stop);

        int min_ind = 0;
        for(unsigned int i=1; i<avg_list.size(); i++)
            if(avg_list[min_ind] > avg_list[i])
                min_ind = i;
        double lambda_best = lambdas[min_ind];
        cout << "n=" << n << ": " << lambda_best << " in " << elapsed_milliseconds/1000. << " seconds " << "(" << nb_tests*(lambda_final-lambda_init) << " problems)" << endl;
    }

    return 0;
}
