#include "lut.h"
#include "circuit.h"

void LUT::cal_update() {
    
    score_fault_update_cost = 0;


    value = !value;

    if(uptag) {
        score_fault_update_cost -= up_cost;
    }
    
    // value sat score
    if(!isPI) {
        vsat = !vsat;
    } 

    for(LUT* out : fanouts) {
        out->vsat = ( out->cal_value() == out->value ); 
    }

    int old_fd[2], old_fpl[2];
    old_fd[0] = fd[0];
    old_fd[1] = fd[1];
    old_fpl[0] = fpl[0];
    old_fpl[1] = fpl[1];

    // fault detected score
    cal_fault_info(fd, fpl);

    // update cost score
    for(LUT* r : reigon) {
        int t_fd[2], t_fpl[2];
        r->cal_fault_info(t_fd, t_fpl);
        if(t_fd[0] == r->fd[0] && t_fd[1] == r->fd[1] && t_fpl[0] == r->fpl[0] && t_fpl[1] == r->fpl[1]) continue;
        
        if(!r->uptag) {
            score_fault_update_cost += r->up_cost;
        }
    }
    
    value = !value;
    if(!isPI) vsat = !vsat;

    fd[0] = old_fd[0];
    fd[1] = old_fd[1];
    fpl[0] = old_fpl[0];
    fpl[1] = old_fpl[1];

    for(LUT* out : fanouts) {
        out->vsat = ( out->cal_value() == out->value );
    }

    score = - score_value_unsatisfied_cost + score_fault_detected_weight + score_fault_propagated_weight - score_fault_update_cost;

    if(!is_good_var && score > 0 && tabu <= C->step) {
        is_good_var = 1;
        C->good_luts.push_back(this);
    }
}

void LUT::cal_score() {

    update_luts.clear();
    
    score = 0;
    score_value_unsatisfied_cost = 0;
    score_fault_detected_weight = 0;
    score_fault_propagated_weight = 0;
    score_fault_update_cost = 0;

    // printf("old_fd: %d %d\n", old_fd[0], old_fd[1]);

    value = !value;

    if(uptag) {
        score_fault_update_cost -= up_cost;
    }
    
    // value sat score
    
    if(!isPI) {
        if(vsat) score_value_unsatisfied_cost += vunat_cost;
        else score_value_unsatisfied_cost -= vunat_cost;
        vsat = !vsat;
    } 

    for(LUT* out : fanouts) {
        if(!out->vsat && out->cal_value() == out->value) {
            score_value_unsatisfied_cost -= out->vunat_cost;
            out->vsat = 1;
        }
        if(out->vsat && out->cal_value() != out->value){
            score_value_unsatisfied_cost += out->vunat_cost;
            out->vsat = 0;
        }
    }

    int old_fd[2], old_fpl[2];
    old_fd[0] = fd[0];
    old_fd[1] = fd[1];
    old_fpl[0] = fpl[0];
    old_fpl[1] = fpl[1];

    // fault detected score

    cal_fault_info(fd, fpl);

    // printf("fd: %d %d %d %d\n", fd[0], fd[1], old_fd[0], old_fd[1]);

    for(LUT* out : fanouts) {
        int in1 = 0, in2 = 0;
        for(int i=0; i<out->fanins.size(); i++) {
            if(out->fanins[i] == this) {
                in1 |= (!value << i);
                in2 |= (value << i);
            } else {
                in1 |= (out->fanins[i]->value << i);
                in2 |= (out->fanins[i]->value << i);
            }
        }

        in1 = (in1 << 1) | out->value;
        in2 = (in2 << 1) | out->value;

        for(int i=out->fanins.size(); i<out->fanins.size()+out->inner_gates.size(); i++) {
            Gate* g = out->inner_gates[i-out->fanins.size()];
            
            int t_fd1[2], t_fpl1[2];
            int t_fd2[2], t_fpl2[2];
            
            t_fd1[0] = out->fault_table[i][in1].fd[0] && out->fd[!out->value];
            t_fd1[1] = out->fault_table[i][in1].fd[1] && out->fd[!out->value];

            t_fd2[0] = out->fault_table[i][in2].fd[0] && out->fd[!out->value];
            t_fd2[1] = out->fault_table[i][in2].fd[1] && out->fd[!out->value];

            t_fpl1[0] = out->fault_table[i][in1].fpl[0] + out->fault_table[i][in1].fd[0] * out->fpl[!out->value];
            t_fpl1[1] = out->fault_table[i][in1].fpl[1] + out->fault_table[i][in1].fd[1] * out->fpl[!out->value];

            t_fpl2[0] = out->fault_table[i][in2].fpl[0] + out->fault_table[i][in2].fd[0] * out->fpl[!out->value];
            t_fpl2[1] = out->fault_table[i][in2].fpl[1] + out->fault_table[i][in2].fd[1] * out->fpl[!out->value];

            score_fault_detected_weight += (t_fd2[0] - t_fd1[0]) * g->fault_detected_weight[0];
            score_fault_detected_weight += (t_fd2[1] - t_fd1[1]) * g->fault_detected_weight[1];

            score_fault_propagated_weight += (t_fpl2[0] - t_fpl1[0]) * g->fault_propagated_weight[0];
            score_fault_propagated_weight += (t_fpl2[1] - t_fpl1[1]) * g->fault_propagated_weight[1];
        }
    }

    int in1 = 0, in2 = 0;
    for(int i=0; i<fanins.size(); i++) {
        in1 |= (fanins[i]->value << i);
    }
    in2 = in1;
    in1 = (in1 << 1) | !value;
    in2 = (in2 << 1) | value;

    for(int i=fanins.size(); i<fanins.size()+inner_gates.size(); i++) {
        Gate* g = inner_gates[i-fanins.size()];

        int t_fd1[2], t_fpl1[2];
        int t_fd2[2], t_fpl2[2];
        
        t_fd1[0] = fault_table[i][in1].fd[0] && old_fd[value];
        t_fd1[1] = fault_table[i][in1].fd[1] && old_fd[value];

        t_fd2[0] = fault_table[i][in2].fd[0] && fd[!value];
        t_fd2[1] = fault_table[i][in2].fd[1] && fd[!value];

        t_fpl1[0] = fault_table[i][in1].fpl[0] + fault_table[i][in1].fd[0] * old_fpl[value];
        t_fpl1[1] = fault_table[i][in1].fpl[1] + fault_table[i][in1].fd[1] * old_fpl[value];

        t_fpl2[0] = fault_table[i][in2].fpl[0] + fault_table[i][in2].fd[0] * fpl[!value];
        t_fpl2[1] = fault_table[i][in2].fpl[1] + fault_table[i][in2].fd[1] * fpl[!value];

        score_fault_detected_weight += (t_fd2[0] - t_fd1[0]) * g->fault_detected_weight[0];
        score_fault_detected_weight += (t_fd2[1] - t_fd1[1]) * g->fault_detected_weight[1];

        score_fault_propagated_weight += (t_fpl2[0] - t_fpl1[0]) * g->fault_propagated_weight[0];
        score_fault_propagated_weight += (t_fpl2[1] - t_fpl1[1]) * g->fault_propagated_weight[1];
    }

    // update cost score
    for(LUT* r : reigon) {
        int t_fd[2], t_fpl[2];
        r->cal_fault_info(t_fd, t_fpl);
        if(t_fd[0] == r->fd[0] && t_fd[1] == r->fd[1] && t_fpl[0] == r->fpl[0] && t_fpl[1] == r->fpl[1]) continue;
        
        if(!r->uptag) {
            score_fault_update_cost += r->up_cost;
            update_luts.insert(r);
            r->add_update_to_this.push_back(this);
        }
    }
    
    value = !value;
    if(!isPI) vsat = !vsat;

    fd[0] = old_fd[0];
    fd[1] = old_fd[1];
    fpl[0] = old_fpl[0];
    fpl[1] = old_fpl[1];

    for(LUT* out : fanouts) {
        out->vsat = ( out->cal_value() == out->value );
    }

    score = - score_value_unsatisfied_cost + score_fault_detected_weight + score_fault_propagated_weight - score_fault_update_cost;

    if(!is_good_var && score > 0 && tabu <= C->step) {
        is_good_var = 1;
        C->good_luts.push_back(this);
    }
}