#include <unordered_set>

#include "circuit.h"

int LUTCircuit::check() {

    // static bool init = 0;
    // static std::unordered_set<Gate*> dt;

    printf("checking circuit ...\n");

    int score_value_unsatisfied_cost = 0;
    int score_fault_detected_weight = 0;
    int score_fault_propagated_weight = 0;
    int score_fault_update_cost = 0;
    int unsatisfied_lut = 0;

    for(LUT* lut : luts) {
        assert(lut->vsat == (lut->value == lut->cal_value()));

        if(!lut->vsat) {
            score_value_unsatisfied_cost += lut->vunat_cost;
            unsatisfied_lut++;
            // printf("vunsat: %s\n", lut->name);
        }

        if(lut->uptag) {
            score_fault_update_cost += lut->up_cost;
        } else {
            int t_fd[2], t_fpl[2];
            lut->cal_fault_info(t_fd, t_fpl);
            assert(t_fd[0] == lut->fd[0]);
            assert(t_fd[1] == lut->fd[1]);
            assert(t_fpl[0] == lut->fpl[0]);
            assert(t_fpl[1] == lut->fpl[1]);
        }

        int input = 0;
        for(int i=0; i<lut->fanins.size(); i++) {
            input |= (lut->fanins[i]->value << i);
        }
        input <<= 1;
        input |= lut->value;

        for(int i=lut->fanins.size(); i<lut->fanins.size()+lut->inner_gates.size(); i++) {
            LUT::FaultInfo &info = lut->fault_table[i][input];
            Gate* g = lut->inner_gates[i-lut->fanins.size()];

            int t_fd[2], t_fpl[2];
            t_fd[0] = info.fd[0] && lut->fd[!lut->value];
            t_fd[1] = info.fd[1] && lut->fd[!lut->value];
            t_fpl[0] = info.fpl[0] + info.fd[0] * lut->fpl[!lut->value];
            t_fpl[1] = info.fpl[1] + info.fd[1] * lut->fpl[!lut->value];
            
            score_fault_detected_weight += t_fd[0] * g->fault_detected_weight[0];
            score_fault_detected_weight += t_fd[1] * g->fault_detected_weight[1];

            score_fault_propagated_weight += t_fpl[0] * g->fault_propagated_weight[0];
            score_fault_propagated_weight += t_fpl[1] * g->fault_propagated_weight[1];
        }
    }

    printf("unsat_lut: %d\n", unsatisfied_lut);
    printf("score_value_unsatisfied_cost: %d\n", score_value_unsatisfied_cost);
    printf("score_fault_detected_weight: %d\n", score_fault_detected_weight);
    printf("score_fault_propagated_weight: %d\n", score_fault_propagated_weight);
    printf("score_fault_update_cost: %d\n", score_fault_update_cost);
    
    int score = - score_value_unsatisfied_cost + score_fault_detected_weight + score_fault_propagated_weight - score_fault_update_cost;

    printf("score: %d\n", score);

    return score;
}