#include "circuit.h"

#include <queue>
#include <unordered_map>
#include <unordered_set>
#include "assert.h"

void Circuit::init_stems() {
    for(auto& gate: gates) {
        if(gate->outputs.size() >= 2) {
            gate->stem = true;
        }
        //gate->stem = true;
        if(gate->stem) {
            stems.push_back(gate);
        }
    }

    for(Gate *g : gates) {
        if(g->isPI) continue;
        std::queue<Gate*> q;
        std::unordered_map<Gate*, bool> used;
        q.push(g);

        while(!q.empty()) {
            Gate* now = q.front();
            q.pop();
            for(Gate* in : now->inputs) {
                if(in->stem) {
                    g->pre_stems.push_back(in);
                } else if(!used[in]) {
                    used[in] = true;
                    q.push(in);
                }
            }
        }
        //printf("pre: %s %d\n", g->name.c_str(), g->pre_stems.size());
    }

    for(Gate *g : gates) {
        if(g->isPO) continue;
        std::queue<Gate*> q;
        std::unordered_map<Gate*, bool> used;
        q.push(g);

        while(!q.empty()) {
            Gate* now = q.front();
            q.pop();
            for(Gate* out : now->outputs) {
                if(out->stem) {
                    g->suc_stems.push_back(out);
                } else if(!used[out]) {
                    used[out] = true;
                    q.push(out);
                }
            }
        }
        //printf("pre: %s %d\n", g->name.c_str(), g->pre_stems.size());
    }
}


void Circuit::init_topo_index() {
    int topo = 1;
    std::queue<Gate*> q;

    std::unordered_map<Gate*, int> ins;
    for(Gate* gate : gates) {
        ins[gate] = gate->inputs.size();
    }

    for(auto in : PIs) {
        in->id = topo++;
        q.push(in);
    }

    while(!q.empty()) {
        Gate* g = q.front(); q.pop();
        for(Gate* out : g->outputs) {
            ins[out]--;
            if(ins[out] == 0) {
                out->id = topo++;
                q.push(out);
            }
        }
    }
}

void Circuit::print_gates() {
    static const char* type2name[9] = {"AND", "NAND", "OR", "NOR", "XOR", "XNOR", "NOT", "BUF", "IN"};
    for(Gate* gate : gates) {
        printf("Gate: %3s (t:%4s v:%d pi:%d po:%d s:%d p:%d s0:%d s1:%d fpl0:%d fpl1:%d)  Inputs:", gate->name.c_str(), type2name[gate->type], gate->value, gate->isPI, gate->isPO, gate->stem, gate->is_propagated(), gate->sa[0], gate->sa[1], gate->fault_propagate_len[0], gate->fault_propagate_len[1]);
        for(Gate* in : gate->inputs) {
            printf(" %s(%d)", in->name.c_str(), gate->is_detected(in));
        }
        printf("\n");
    }
}

bool Circuit::is_valid_circuit() {

    ll flip_total_weight = 0;
    ll stem_total_weight = 0;
    ll fault_total_weight = 0;

    int flip_total_cnt = 0;
    int stem_total_cnt = 0;
    int fault_total_cnt = 0;

    //printf("flip: %d, stem: %d, fault:%d\n", flip_total_weight, stem_total_weight, fault_total_weight);

    for(Gate* g : gates) {

        if(flip_need_update[g->id]) {
            flip_total_weight += flip_weight[g->id];
            flip_total_cnt++;
        }

        if(g->stem && g->cal_value() != g->value) {
            stem_total_weight += stem_weight[g->id];
        }

        if(g->stem && g->cal_value() == g->value) {
            stem_total_cnt++;
        }

        if(g->cal_propagate_len(0) != g->fault_propagate_len[0] || g->cal_propagate_len(1) != g->fault_propagate_len[1]) {
            printf("WRONG-PRO-LEN: %s \n", g->name.c_str());
            print_gates();
            return false;
        }

        if(g->sa[0]) {
            fault_total_weight += fault_weight[g->id][0];
            fault_total_cnt += 1;
        }

        if(g->sa[1]) {
            fault_total_weight += fault_weight[g->id][1];
            fault_total_cnt += 1;
        }

        // 检查门的赋值情况
        if(g->cal_value() != g->value) {
            printf("WRONG-ASSGIN: %s \n", g->name.c_str());
            return false;
        }
        
        // 检查 PO 的传播设定是否正确
        if(g->isPO) {
            if(g->sa[g->value] != 0 || g->sa[!g->value] == 0 ) {
                printf("WRONG-PO: %s \n", g->name.c_str());
            }
            continue;
        }

        // 非 PO 情况下检查故障传播是否正确

        bool sa0 = false;
        bool sa1 = false;

        for(Gate* out : g->outputs) {
            if(out->cal_value() != out->value) {
                assert(out->stem);
                continue;
            }

            g->value = !g->value;

            if(out->cal_value() != out->value) {
                sa0 |= out->is_propagated() && !g->value;
                sa1 |= out->is_propagated() && g->value;
            }

            g->value = !g->value;
        }

        if(sa0 != g->sa[0] || sa1 != g->sa[1]) {
            printf("WRONG-SA: %s \n", g->name.c_str());
            return false;
        }
    }

    if(this->flip_total_weight != flip_total_weight || this->stem_total_weight != stem_total_weight || this->fault_total_weight != fault_total_weight) {
        printf("CIRCUIT CHECK FAILED!\n");
        printf("[wrong] flip: %d, stem: %d, fault:%d\n", this->flip_total_weight, this->stem_total_weight, this->fault_total_weight);
        printf("[right] flip: %d, stem: %d, fault:%d\n", flip_total_weight, stem_total_weight, fault_total_weight);
        return false;
    }

    if(this->flip_total_cnt != flip_total_cnt || this->stem_total_cnt != stem_total_cnt || this->fault_total_cnt != fault_total_cnt) {
        printf("CIRCUIT CHECK FAILED!\n");
        printf("[wrong] flip_cnt: %d, stem_cnt: %d, fault_cnt:%d\n", this->flip_total_cnt, this->stem_total_cnt, this->fault_total_cnt);
        printf("[right] flip_cnt: %d, stem_cnt: %d, fault_cnt:%d\n", flip_total_cnt, stem_total_cnt, fault_total_weight);
        return false;
    }

    return true;
}


