#include "EA.h"

EA::EA(double proba, int lda, int mu, std::default_random_engine& randomizer) {
    m_proba = proba;
    m_lambda = lda;
    m_mu = mu;

    m_randomizer = randomizer;
    m_is_prepared = false;
    x_tab = nullptr;
}

void EA::prepare_tab() {
    if(m_is_prepared) return;

    x_tab = new Candidate*[m_mu+m_lambda];
    for(int i=0; i<m_mu+m_lambda; i++) {
        int* x = m_problem->get_random_candidate();
        x_tab[i] = new Candidate(x, 0);
    }

    m_binomial_dist = std::binomial_distribution<int>(m_problem->get_max_radius(), m_proba);
    m_is_prepared = true;
}

Result EA::run() {
    MasterMind* pb = m_problem;
    prepare_tab();

    // Initial parents
    for(int i=0; i<m_mu; i++) {
        int* x = pb->get_random_candidate();
        x_tab[i]->sol = x;
        x_tab[i]->value = pb->try_candidate(x);
    }
    std::sort(x_tab, x_tab+m_mu, cmp_candidates);

    Candidate* x_best = x_tab[0];
    int nb_calls = m_mu;

    while(! pb->is_optimal(x_best->sol, x_best->value)) {
        for(int i=0; i<m_lambda; i++) {
            int r = m_binomial_dist(m_randomizer);
            Candidate* y = x_tab[m_mu+i];
            if(m_mu == 1) {
                pb->copy_candidate(x_tab[0]->sol, y->sol);
            } else {
                //pb->crossover(y->sol, x_tab, m_mu);
            }
            pb->modify_with_radius(y->sol, r);
            y->value = pb->try_candidate(y->sol);
        }

        std::sort(x_tab, x_tab+(m_mu+m_lambda), cmp_candidates);
        x_best = x_tab[0];
        nb_calls += m_lambda;
    }

    int* final_sol = pb->get_random_candidate();
    pb->copy_candidate(x_best->sol, final_sol);
    return Result(final_sol, x_best->value, nb_calls);
}

EA::~EA() {
    if(m_is_prepared) {
        for(int i=0, s=m_mu+m_lambda; i<s; i++) {
            Candidate* y = x_tab[i];
            delete[] y->sol;
            delete y;
        }
        delete[] x_tab;
    }
}
