#pragma once

#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>

using ll = long long;

class Gate {
public:
    int id;
    std::string name;
    enum Type { AND, NAND, OR, NOR, XOR, XNOR, NOT, BUF, INPUT } type;
    int value;
    bool sa[2];
    bool stem;
    bool isPI;
    bool isPO;

    int fault_propagate_len[2];

    std::vector<Gate*> pre_stems;
    std::vector<Gate*> suc_stems;

    std::vector<Gate*> outputs;
    std::vector<Gate*> inputs;

    bool is_propagated();
    int cal_value();
    bool cal_sa(bool x);
    bool is_detected(Gate* one_of_input);
    int cal_propagate_len(bool x);
};

class Fault {
public:
    Gate* gate;
    enum Type { SA0, SA1 } type;
    Fault(Gate* gate, Type type):gate(gate),type(type) {}
};

class Circuit {
public:
std::vector<Gate*> PIs;
std::vector<Gate*> POs;
std::vector<Gate*> gates;
std::vector<Gate*> stems; // PI + stems

std::unordered_map<std::string, Gate*> name2gate;

std::queue<Gate*> tmp;
std::unordered_map<Gate*, bool> tmp_used;

void parse_from_file(const char *filename);
void print_gates();

bool is_valid_circuit();

void init_topo_index();
void init_stems();

// local search

bool local_search(std::unordered_set<Fault*> &faults);

// incremental flip struct

const double SP = 0.01;

const int FLIP_INC = 1;
const int FLIP_WEIGHT_MAX = 1e9;

int* CC;

ll flip_total_weight;
int flip_total_cnt;
int* flip_weight;
int* flip_need_update;
std::vector<Gate*> flip_update_queue;

// incremental stem struct
const int STEM_INC = 100;
const int STEM_WEIGHT_MAX = 1e9;
ll stem_total_weight;
int stem_total_cnt;
int* stem_weight;
int* stem_satisfied;

int fault_propagate_tatal_len;

const int FAULT_INC = 1;
const int FAULT_WEIGHT_MAX = 20;
ll fault_total_weight;
int fault_total_cnt;
int** fault_weight;
int** fault_detected;

void ls_init_circuit();
void ls_init_weight(const std::unordered_set<Fault*> &faults);
void ls_update_weight();
void ls_init_data_structs();

void ls_block_recal(Gate* stem);

void ls_flip(Gate* stem);
void ls_update(Gate* stem);
ll ls_pick_score(Gate* stem);

ll ls_score();

int** simulate();

};