#include "gate.h"

bool Gate::merge_cuts(Cut& cut, const Cut& cut1, const Cut& cut2, unsigned int K) {
	cut.gates = cut1.gates;
	size_t size = cut.gates.size();
	
	for (std::set<Gate*>::const_iterator it = cut2.gates.begin(); it != cut2.gates.end(); ++it) {
		cut.gates.insert(*it);
	}

	if (cut.gates.size() > K) 
		return false;

	if (cut1.depth_of_lut > cut2.depth_of_lut) {
		cut.depth_of_lut = cut1.depth_of_lut;
	} else {
		cut.depth_of_lut = cut2.depth_of_lut;
	}

	cut.trivial = false;	
	cut.ozn = compute_signature(cut);
	return true;
}

unsigned int Gate::compute_signature (const Cut& cut) {
	unsigned sum = 0;
	for (std::set<Gate*>::const_iterator it = cut.gates.begin(); it != cut.gates.end(); ++it) {
		unsigned int mocnitel = (*it)->get_id() % 32;
		if (mocnitel == 0) 
				sum += 1;
			else
				sum += 2 << (mocnitel-1);
	}
	return sum;
}

void Gate::compute_cuts(const unsigned int K) {
	if (inputs.empty()) {				// primarni uzel
			Cut* trivialCut = new Cut();
			trivialCut->trivial = true;
			trivialCut->depth_of_lut = 0;
			trivialCut->gates.insert(this);
			trivialCut->ozn = compute_signature(*trivialCut);
			cuts.push_back(trivialCut);
			depth_of_lut = 0;
	} else {
		if (inputs.size() == 1) {		// jednovstupove hradlo
			Cut* trivialCut = new Cut(); 
			trivialCut->gates.insert(this);
			trivialCut->trivial = true;
			trivialCut->ozn = compute_signature(*trivialCut);
			cuts.push_back(trivialCut);

			Gate* predcessor = inputs.front();
			for (std::list<Cut*>::iterator it=predcessor->cuts.begin(); it!=predcessor->cuts.end(); ++it) {
				Cut* c = new Cut((**it));
				cuts.push_back((c));
			}

			if (predcessor->get_inputs().empty())
				depth_of_lut = 1;
			else
				depth_of_lut = predcessor->depth_of_lut;
			trivialCut->depth_of_lut = depth_of_lut;
		} 
		else { //vsechna ostatni dvouvstupova hradla
			Gate* g1 = inputs.front();
			Gate* g2 = inputs.back();
			for (std::list<Cut*>::iterator it1 = g1->cuts.begin(); it1 != g1->cuts.end(); ++it1) //kombinace rezu vstupnich uzlu pocitaneho hradla
				for (std::list<Cut*>::iterator it2 = g2->cuts.begin(); it2 != g2->cuts.end(); ++it2){
					if (zjisti_k_vhodnost(**it1,**it2, K)) // zjistovano pomoci znaceni rezu, pokud false-s urcitosti vime, ze neni vhodny, pakud true->nevim nic
					{
						Cut* cut = new Cut();
						if (merge_cuts(*cut, **it1, **it2, K) ) {	// jestli sloucene rezy vhodne
								if (cuts.empty() || cut->depth_of_lut < depth_of_lut)  //pokud vypocet prvni mnoziny nebo mnozina s mensi hloubkou
									depth_of_lut = cut->depth_of_lut;	//nastav hloubku lutu na hloubku mnoziny (pozdeji zvysena o 1)
								cuts.push_back (cut);					//vloz rez do mnoziny rezu
						} else
							delete(cut);							//smaz nevhodny rez
					}
				}
		
				//remove_equal_and_dominated();  // funkci, ale zpusobuje delsi dobu vypoctu
				depth_of_lut++;
				Cut* trivialCut = new Cut();
				trivialCut->gates.insert(this);
				trivialCut->trivial = true;
				trivialCut->depth_of_lut = depth_of_lut;
				trivialCut->ozn = compute_signature(*trivialCut);
				cuts.push_back(trivialCut);			
		}
	}
}

bool Gate::are_computed_inputs_cuts() {
	for (std::list<Gate*>::iterator it = inputs.begin(); it != inputs.end(); ++it) {
		if ((*it)->cuts.empty()) 
			return false;
	}
	return true;
}

Cut& Gate::get_cut_minimum_delay(void){
	for (std::list<Cut* >::iterator it = cuts.begin(); it != cuts.end(); ++it) {
		std::set<Gate*>::const_iterator result = (*it)->gates.find(this);
		if (result == (*it)->gates.end() && ((*it)->depth_of_lut == depth_of_lut-1) )  //jestli v rezu neni sam uzel obsazen a jeho hloubka je minimalni
			return **it;
	}
	return *cuts.front(); //toto by nikdy nemelo nastat
}

True_table* Gate::compute_true_table(const Cut& c, int hodn) {
	True_table* t_table = new True_table();
	
	std::set<Gate*>::const_iterator result = c.gates.find(this);
	
	if (result == c.gates.end()) {
	if (hodn ==1)
	switch (type) {
		case G_AND: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;						
					}
		case G_NAND: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			delete (t2);
			break;
					 }
		case G_OR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			delete (t2);
			break;
				   }
		case G_NOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					}
		case G_XOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,0);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					}
		case G_XNOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,1);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					 }
		case G_INV: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
			
					}
		case G_BUF: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
					}		
	}

	if (hodn ==0) {
		switch (type) {
		case G_AND: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			delete (t2);
			break;					
					}
		case G_NAND: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;		
					 }
		case G_OR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1,t2);
			
			delete (t1);
			delete (t2);
			break;
				   }
		case G_NOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			break;	
					}
		case G_XOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,1);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					}
		case G_XNOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,0);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					 }
		case G_INV: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
			
					}
		case G_BUF: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
					}		
	}
	}
	} else { // navraceni prvdivostni tabulky
		Row* r= new Row();
		(*r)[id] = hodn;
		t_table->push_back(r);
		}
	return t_table;

}

bool Gate::merge_rows (Row& r, Row* r1, Row* r2) {
	r.clear();
	r = (*r1);
	for (Row::iterator it = r2->begin(); it != r2->end(); ++it) {
		Row::iterator result = r.find((*it).first);
		if ((result != r.end()) && ((*result).second != (*it).second)) {
			return false;
		}
		else {
			r[(*it).first] = (*it).second;
		}
	}
	return true;
}

void Gate::merge_tables(True_table *t, True_table *t1, True_table *t2) {
	Row* r = new Row();
	for (True_table::iterator it1 = t1->begin(); it1 != t1->end(); ++it1) {
		for (True_table::iterator it2 = t2->begin(); it2 != t2->end(); ++it2) {
			Row* r = new Row();
			if (merge_rows(*r, *it1, *it2) ) { //sli sloucit
				t->push_back(r);
			}
			else {
				delete(r);
			}			
		}
	}

}

bool Gate::zjisti_k_vhodnost(Cut& c1, Cut&c2, unsigned int K) {
	unsigned int number = c1.ozn | c2.ozn;
	unsigned int number_of_ones = 0;
	unsigned int x;
	for (int i = 0; i < 32; ++i) {
		x = number & 1;
		if (x == 1)
			++number_of_ones;
		number = number >> 1;
	}
	if (number_of_ones <= K) 
		return true;
	else
		return false;
}

Gate::~Gate() {
	for(std::list<Cut*>::iterator it_cut = cuts.begin(); it_cut != cuts.end(); ++it_cut)
		if (*it_cut != NULL) {
			delete (*it_cut);
			*it_cut = NULL;
		}
}

Lut::~Lut() {
	
		for (True_table::iterator it_row = true_table->begin(); it_row != true_table->end(); ++it_row)
			delete(*it_row);
		delete (true_table);
}

void Gate::add_name_of_input(char *name){
	name_of_inputs.push_back(name);
}

void Gate::set_type(Type_of_gate _type) {
	type=_type;
}

void Gate::add_input(Gate *gat) {
	inputs.push_back(gat);
}

void Gate::add_output(Gate *gat) {
	outputs.push_back(gat);
}

unsigned int Gate::get_depth() const {
	return depth;
}

unsigned int Gate::get_depth_of_lut() const {
	return depth_of_lut;
}

unsigned int Gate::get_id() const {
	return id;
}

void Gate::set_depth(unsigned int D) {
	depth = D;
}

Type_of_gate Gate::get_type(void) const {
	return type;
}

Gate::Gate(bool _k_bounded):depth_of_lut(0) {
		char buf[256];
		sprintf_s(buf, "L%d", c_id); 
		name = buf; 
		id=c_id++; 
		k_bounded = _k_bounded;
}

Gate::Gate(char* _name, bool _k_bounded):name(_name),depth_of_lut(0){
	id=c_id++; k_bounded=_k_bounded;
}

Cut::Cut(Cut &c) {
	for (std::set<Gate*>::iterator it = c.gates.begin(); it != c.gates.end(); ++it) {
		gates.insert(*it);
	}
	depth_of_lut = c.depth_of_lut;
	ozn = c.ozn;
	trivial = c.trivial;
}

bool Gate::equal(Cut& cut1, Cut& cut2){
	std::set<Gate*>::iterator result;
	
	if (cut1.ozn == cut2.ozn) {
		if (cut1.gates.size() == cut2.gates.size())
			for (std::set<Gate*>::iterator it = cut1.gates.begin(); it != cut1.gates.end(); ++it) { 
				result = cut2.gates.find(*it);
				if (result == cut2.gates.end())
					return false;
			}
	} else
		return false;

	return true;
}

bool Gate::dominate(Cut &cut1, Cut &cut2){
	std::set<Gate*>::iterator result;
	int x = cut1.ozn & cut2.ozn;
	if (x == cut1.ozn) {
		for (std::set<Gate*>::iterator it = cut1.gates.begin(); it != cut1.gates.end(); ++it) { 
				result = cut2.gates.find(this);
				if (result == cut2.gates.end())
					return false;
			}
	}
	else 
		return false;
	return true;
}


void Gate::remove_equal_and_dominated(void) {	


	std::list<Cut*>::iterator cut1 = cuts.begin();
	std::list<Cut*>::iterator cut2;
	std::list<Cut*>::iterator cut_ke_smazani;
	bool smazat;

	while (cut1 != cuts.end()) {
		cut2 = cut1;
		++cut2;
		smazat = false;
		while (cut2 != cuts.end()) {
			if (equal (**cut1, **cut2)) {
				cut_ke_smazani = cut1;
				smazat = true;
				++odstraneno;
				break;
				
			} else
			if (dominate (**cut1, **cut2)) {
				cut_ke_smazani = cut2;
				smazat = true;
				++odstraneno;
				break;
			} else
			if (dominate (**cut2, **cut1)) {
				cut_ke_smazani = cut1;
				++odstraneno;
				smazat = true;
				break;
			}
			++cut2;
		
		}

		++cut1;
		if (smazat) {
			if (cut_ke_smazani == cut1) {
				++cut1;
			}
			delete (*cut_ke_smazani);
			cuts.remove(*cut_ke_smazani);
			smazat = false;
		}
	}		
}
