#include "crossover-rate.h"

SearchHeuristic* CrossoverProbaFromLambdaOptimizer::get_context(unsigned int i, double parameter, std::default_random_engine& randomizer) {
    //GA* algo = new GA(lambda_initial+i, lambda_initial+i, PLUS, 1./n, parameter, UNIFORM_CROSSOVER, GA_V1, randomizer);
    GA* algo = new GA(lambda_initial+i, 1, PLUS, 1./n, parameter, UNIFORM_CROSSOVER, GA_V2, randomizer);
    MasterMind* mm = new MasterMind(n, n, randomizer);
    algo->set_problem(mm);
    return algo;
}
std::string CrossoverProbaFromLambdaOptimizer::display_context(unsigned int num_context) {
   std::stringstream ss;
   ss << "lambda=" << lambda_initial+num_context;
   return ss.str();
}
std::string CrossoverProbaFromLambdaOptimizer::get_context_for_csvfile(unsigned int num_context) {
   return std::to_string(lambda_initial+num_context);
}

void script_crossover_proba_global_optim(std::default_random_engine& randomizer, std::string namefile, bool restart, unsigned int restart_n, unsigned int restart_lambda) {
    if(! restart)
        std::ofstream savefile(namefile);
    double global_p_init=0.01, global_p_final=0.99, global_p_step=0.02;
    ConfidenceInterval ci(global_p_init, 0, global_p_final);

    unsigned int n_initial = (restart) ? restart_n : 5;
    for(unsigned int n=n_initial; n<=20; n++) {
        unsigned int lda_initial = (restart && n==n_initial) ? restart_lambda : 2;
        for(unsigned int lda=lda_initial; lda<=100; lda++) {
            std::cout << "n=" << n << ", lambda=" << lda << std::endl;

            double p_init = (ci.down - 0.25 >= global_p_init) ? ci.down - 0.25 : global_p_init;
            double p_final = (ci.up + 0.25 <= global_p_final) ? ci.up + 0.25 : global_p_final;
            double p_step = (ci.up >= 0.95 && ci.down >= 0.75) ? global_p_step/2 : global_p_step;

            CrossoverProbaFromLambdaOptimizer cpflo(n, lda, randomizer);
            cpflo.set_nb_contexts(1);
            cpflo.set_study_interval(p_init, p_final, p_step);
            cpflo.add_test_wave(40, 2.5419828669, 0);
            cpflo.add_test_wave(400, 2.4429370465, 1);
            cpflo.add_test_wave(2000, 2.4344837209, 2);
            std::vector<ConfidenceInterval> cis = cpflo.run(false);
            ci = cis[0];

            std::ofstream file(namefile, std::ios::app);
            file << n << ";" << lda << ";" << ci.down << ";" << ci.mean << ";" << ci.up << ";" << ci.thresold << std::endl;
        }
    }
}

double crossover_model_linearization(double x) {
    return log(1/(1-x));
}

void crossover_model_filtering(std::string input, std::string output, std::string modeling, int filtered_n, int filtered_lda) {
    std::ifstream infile(input);
    std::ofstream outfile(output);
    std::ofstream out2file(modeling);

    std::string line;
    while(getline(infile, line)) {
        int n, lda;
        double pmin, pbest, pmax, thresold;
        sscanf(line.c_str(),"%d;%d;%lf;%lf;%lf;%lf", &n, &lda, &pmin, &pbest, &pmax, &thresold);

        if((filtered_n==0 | n == filtered_n) && (filtered_lda==0 | lda == filtered_lda)) {
            if(lda < 5) continue;

            outfile << line << std::endl;
            out2file << lda << ";" << crossover_model_linearization(pmin) << ";" << crossover_model_linearization(pbest) << ";" << crossover_model_linearization(pmax) << ";" << std::endl;
        }
    }

    std::cout << "Filtering finished !" << std::endl;
}

struct Data {
    Data(int n0, int lda0, double pmin0, double pmax0) : n{n0}, lda{lda0}, pmin{pmin0}, pmax{pmax0} {};
    int n;
    int lda;
    double pmin;
    double pmax;
};

void crossover_model_optimization(std::string namefile) {
    std::vector<Data> datas;

    // Load data
    std::string line;
    std::ifstream infile(namefile);
    while(getline(infile, line)) {
        int n, lda;
        double pmin, pbest, pmax, thresold;
        sscanf(line.c_str(),"%d;%d;%lf;%lf;%lf;%lf", &n, &lda, &pmin, &pbest, &pmax, &thresold);
        datas.push_back(Data(n, lda, pmin, pmax));
    }

    // Find the values which give the best performance
    double best_performance=0;
    double best_a=0, best_b=0, best_c=0, best_d=0;
    for(double d=0.1433; d<=0.1433; d+=0.001) {
        for(double c=2.10; c<=2.50; c+= 0.01) { // 1.52 --> 2.42
            double best2_performance=0;
            double best2_a=0, best2_b=0, best2_c=0, best2_d=0;

            for(double a=0.2; a<0.6; a+=0.01) {
                for(double b=-3; b<3; b+=0.01) {
                    int good = 0;
                    int bad = 0;

                    // Calculate the performance
                    for(int i=0, s=datas.size(); i<s; i++) {
                        Data dat = datas[i];
                        double prev = 1-exp(-exp(-d*dat.n-c)*dat.lda + exp(-dat.n*a+b));
                        if(dat.lda>=5) {
                            if(dat.pmin <= prev && prev <= dat.pmax)
                                good++;
                            else
                                bad++;
                        }
                    }

                    // Keep the best performance
                    double perf = (double) good / (bad+good);
                    if(perf > best2_performance) {
                        best2_a = a;
                        best2_b = b;
                        best2_c = c;
                        best2_d = d;
                        best2_performance = perf;
                    }
                }
            }
            if(best_performance < best2_performance) {
                best_a = best2_a;
                best_b = best2_b;
                best_c = best2_c;
                best_d = best2_d;
                best_performance = best2_performance;
            }
            std::cout << " -> " <<  best2_d << ", " << best2_c << ": " << best2_performance*100 << " (" << best2_a << "," << best2_b << ")" << std::endl;
        }
    }
    std::cout << "BEST: a=" << best_a << ", b=" << best_b << ", c="<< best_c << ", d=" << best_d << " ==> " << best_performance*100 << std::endl;
}

void crossover_model_validation(std::string namefile, double slope1, double intercept1, double slope2, double intercept2) {
    int good = 0;
    int bad = 0;
    std::ifstream infile(namefile);

    std::string line;
    while(getline(infile, line)) {
        int n, lda;
        double pmin, pbest, pmax, thresold;
        sscanf(line.c_str(),"%d;%d;%lf;%lf;%lf;%lf", &n, &lda, &pmin, &pbest, &pmax, &thresold);

        double prev = 1-exp(-exp(slope1*n+intercept1)*lda + exp(slope2*n+intercept2));
        if(lda>=5) {
            if(pmin <= prev && prev <= pmax)
                good++;
            else
                bad++;
        }
    }
    std::cout << "Modeling: " << (double) good / (bad+good) << std::endl;
}

double cr(int n, int lambda) {
    return 1-exp(-exp(-0.1433*n-2.15)*lambda + exp(-0.3*n-0.22));
}
