#include <set>
#include <assert.h>

#include "circuit.h"
#include "paras.h"


void LUTCircuit::print() {

    std::set<Gate*> st;

    int total_gate = 0;

    printf("PIs: ( ");
    for(LUT* lut : PIs) {
        printf("%s ", lut->gate->name.c_str());
    }
    printf(")\n");
    printf("POs: ( ");
    for(LUT* lut : POs) {
        printf("%s ", lut->gate->name.c_str());
    }
    printf(")\n");

    for(LUT* lut : luts) {

        printf("[v:%d vs:%d fd0:%d fd1:%d] ", lut->value, lut->vsat, lut->fd[0], lut->fd[1]);
        printf("lut: %s inputs: ( ", lut->gate->name.c_str());

        total_gate += lut->inner_gates.size();

        for(LUT* in : lut->fanins) {
            printf("%s ", in->gate->name.c_str());
        }
        printf(")\n");
    }

    printf("total gate: %d\n", total_gate);
}


LUTCircuit* Circuit::build_lut_circuit() {

    LUTCircuit* C = new LUTCircuit();

    std::unordered_map<Gate*, LUT*> gate2LUT;

    std::queue<Gate*> q;
    std::set<Gate*> done;

    for(Gate* gate : gates) {
        // assert fanouts >= 2
        if(gate->fanouts.size() >= 2 || gate->isPI || (gate->isPO && gate->fanouts.size() == 0)) {
            q.push(gate);
            done.insert(gate);
        }
    }

    while(!q.empty()) {
        Gate* gate = q.front();
        q.pop();

        LUT* lut = new LUT(gate, C);
        
        gate2LUT[gate] = lut;
        
        // printf("size: %d lut: %s inputs: ( ", lut->inner_gates.size(), lut->gate->name.c_str());
        // for(Gate* in : lut->__gate_fanins) {
        //     printf("%s ", in->name.c_str());
        // }
        // printf(") [");

        // for(Gate* inner : lut->inner_gates) {
        //     printf("%s ", inner->name.c_str());
        // }
        // printf("]\n");

        for(Gate* in : lut->__gate_fanins) {
            if(in->fanouts.size() < 2 && !in->isPI && done.count(in) == 0) {
                done.insert(in);
                q.push(in);
            }
        }

        C->luts.push_back(lut);
    }

    for(LUT* lut : C->luts) {

        lut->name = lut->gate->name.c_str();

        std::sort(lut->inner_gates.begin(), lut->inner_gates.end(), [](Gate* a, Gate* b) {
            return a->id < b->id;
        });

        for(Gate* in : lut->__gate_fanins) {

            // if(gate2LUT.count(in) == 0) {
            //     printf("error: %s %s\n", in->name.c_str(), lut->name);
            //     // exit()
            // }
            assert(gate2LUT.count(in) > 0);
            

            lut->fanins.push_back(gate2LUT[in]);
            gate2LUT[in]->fanouts.push_back(lut);
        }

        if(lut->isPI) {
            C->PIs.push_back(lut);
            lut->isPI = 1;
        }
        if(lut->isPO) {
            C->POs.push_back(lut);
            lut->isPO = 1;
        }
    }

    for(LUT* lut : C->luts) {
        C->rtopo_luts.push_back(lut);
    }

    std::sort(C->luts.begin(), C->luts.end(), [](LUT* a, LUT* b) {
        return a->gate->id < b->gate->id;
    });

    std::sort(C->rtopo_luts.begin(), C->rtopo_luts.end(), [](LUT* a, LUT* b) {
        return a->gate->rtopo < b->gate->rtopo;
    });

    return C;
}

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->fanins.size();
    }

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

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

    // 计算反向拓扑序
    topo = 1;
    std::unordered_map<Gate*, int> outs;

    for(auto out : POs) {
        out->rtopo = topo++;
        q.push(out);
    }

    while(!q.empty()) {
        Gate* g = q.front(); q.pop();
        rtopo_gates.push_back(g);
        for(Gate* in : g->fanins) {
            outs[in]++;
            if(outs[in] == in->fanouts.size()) {
                in->rtopo = topo++;
                q.push(in);
            }
        }
    }
}