#include "collapsing.h"
#include "location.h"
#include "structural.h"
#include <set>
#include <algorithm>
#include <time.h>
#include <process.h>
#include <fstream>


using namespace hub;
using namespace std;


TVnejsi* Collapsing::minim(TVnejsi* nove) {  // minimalizuje SOP
	if (atoi(arg[2]) ==-1 || atoi(arg[2]) >= nove->clenu) return nove;  // arg[2]: -1 -zadna, 1 -vsechny, 2..x -vic nez 2..x clenu
#ifdef DEBUG
	clock_t s = clock();
#endif
	TVnejsi* nove2;
	int clenu;
	FILE *soubor = fopen("pom.pla","w");
	fprintf(soubor,".i %d\n",INPUT);
	fprintf(soubor,".o 1\n");
//	fprintf(soubor,".p %d\n",nove->clenu);
	int i=0;
	while (nove->vnejsi[i] != NULL) {
		for (int j=0; j<INPUT; j++)
			switch (nove->vnejsi[i]->vnitrni2[j]) {
				case 0: fprintf(soubor,"-"); break;
				case 1: fprintf(soubor,"1"); break;
				case -1: fprintf(soubor,"0"); break;
			}
		fprintf(soubor," 1\n");
		i++;
	}
	fprintf(soubor,".e\n");
	fclose(soubor);
//	char   psBuffer[128];
//	FILE   *pPipe;
//	if( (pPipe = _popen( "espresso pom.pla","rt" )) == NULL ) exit(1);
//	fstream f("pom2.pla",ios::out);
//	while(fgets(psBuffer, 128, pPipe)) f << psBuffer; /* Read pipe until end of file, or an error occurs. */
//	f.close();
	system("espresso < pom.pla > pom2.pla");
	soubor = fopen("pom2.pla","r");
	char c;
	int d;
	fscanf(soubor,"%c%c%d\n%c%c%d\n%c%c%d\n",&c,&c,&d,&c,&c,&d,&c,&c,&clenu);
	nove2 = new TVnejsi(clenu+1);
	for (int i=0; i<clenu; i++) {
		nove2->vnejsi[i] = new TVnitrni(INPUT);
		for (int j=0; j<INPUT; j++)
			switch (getc(soubor)) {
				case '-': nove2->vnejsi[i]->vnitrni2[j]=0; break;
				case '1': nove2->vnejsi[i]->vnitrni2[j]=1; break;
				case '0': nove2->vnejsi[i]->vnitrni2[j]=-1; break;
			}
		fscanf(soubor,"%c%c\n",&c,&c);
	}
	fclose(soubor);
	delete nove;
#ifdef DEBUG
		casEsp += clock() - s;
		pocEsp++;
#endif

	return nove2;
}


void Collapsing::absorpce(TVnejsi* nove, int* novy_soucin) {  // pouzije se absorpcni pravidlo A + A*B = A
  if (strcmp(arg[4],"true") == 0) {
#ifdef DEBUG
	clock_t s = clock();
#endif
	int starsi_souciny,k;
	int iterator;
	bool nalezen=false, AB=true;  // AB znaci A-B
	starsi_souciny = *novy_soucin-1;
	while (!nalezen && starsi_souciny>=0)
		if (AB) {
			iterator = 0;
			while (iterator!=INPUT && (nove->vnejsi[starsi_souciny]->vnitrni2[iterator] == nove->vnejsi[*novy_soucin]->vnitrni2[iterator] || nove->vnejsi[starsi_souciny]->vnitrni2[iterator] == 0)) iterator++;  // pokud jsou stejne prvky nebo v starsi_souciny neni promenna, tak muzeme novy_soucin rusit
			if (iterator == INPUT) {  // A-B == 0, tj. B ma nektere prvky navic oproti A, a B muzem zrusit
				delete nove->vnejsi[*novy_soucin];
				nove->vnejsi[*novy_soucin]=NULL;
				(*novy_soucin)--;
				nalezen = true;
			}
			else {
				if (starsi_souciny>0) starsi_souciny--;
				else {AB = false; starsi_souciny = *novy_soucin -1;}
			}
		}
		else {  // B-A
			iterator = 0;
			while (iterator!=INPUT && (nove->vnejsi[starsi_souciny]->vnitrni2[iterator] == nove->vnejsi[*novy_soucin]->vnitrni2[iterator] || nove->vnejsi[*novy_soucin]->vnitrni2[iterator] == 0)) iterator++;  // pokud jsou stejne prvky nebo v novy_soucin neni promenna, tak muzeme stary_soucin rusit
			if (iterator == INPUT) {  // A-B == 0, tj. B ma nektere prvky navic oproti A, a B muzem zrusit
				delete nove->vnejsi[starsi_souciny];
				for ( k=starsi_souciny; k<*novy_soucin; k++) nove->vnejsi[k]=nove->vnejsi[k+1];
				nove->vnejsi[*novy_soucin]=NULL;
				(*novy_soucin)--; starsi_souciny--;
			}
			else {
				starsi_souciny--;
			}
		}
#ifdef DEBUG
		casAbs += clock() - s;
		pocAbs++;
#endif
  }
}

TVnejsi* Collapsing::my_and(THradlo* hr) {  // (ab+cd) AND (ef+gh) = abef + abgh + cdef + cdgh
		TVnejsi* nove = new TVnejsi;
		int i,j,poc_vst_int=hr->poc_vst;
		bool zrus;
		int *vynasobeno = new int[poc_vst_int];  // pole prave nasobenych clenu
		for (i=0; i<poc_vst_int; i++) vynasobeno[i]=0;
		int promenna;  // iterator prochazejici promenne v mnozine
#ifndef NSTEJNA
		bool *stejny_clen = new bool[poc_vst_int];  // pole urcujici zda promenna tam uz je, pokud true, tak se nepude na dalsi promennou v clenu ale na konec
		for (j=0; j<poc_vst_int; j++) stejny_clen[j]=false;
#endif
		int novy_soucin=0;
		while (hr->hradlo[0]->vnejsi[vynasobeno[0]] != NULL) {  // dokud je v prvnim nasobenem clenu este co nasobit
			nove->vnejsi[novy_soucin] = new TVnitrni(INPUT);
			zrus = false;
			for (i=0; i<poc_vst_int && !zrus; i++) {  // tolik clenu se vzdy vynasobi
				promenna = 0;
				while (hr->hradlo[i]->vnejsi[vynasobeno[i]]->vnitrni2[promenna] == 0) promenna++;  // iterator na prvni promennou v mnozine
#ifndef NSTEJNA
				stejny_clen[i] = true; // nastavim, a pokud alespon jedna promenna v clenu nebude stejna s uz vlozenou promennou,tak se da pak false a dalsi cleny nelze absorbovat
#endif
				while (promenna != INPUT && !zrus) {  // prepisu promenne
					if (nove->vnejsi[novy_soucin]->vnitrni2[promenna] == -hr->hradlo[i]->vnejsi[vynasobeno[i]]->vnitrni2[promenna]) {  // mam dat promennou, ale pokud uz tam je ta znegovana, tak je vysledek logicka 0
						delete nove->vnejsi[novy_soucin];
						nove->vnejsi[novy_soucin]=NULL;
						zrus = true;
#ifndef NSTEJNA
						stejny_clen[i] = false;
#endif
					}
					else
						if (nove->vnejsi[novy_soucin]->vnitrni2[promenna] == hr->hradlo[i]->vnejsi[vynasobeno[i]]->vnitrni2[promenna]) {
							promenna++;
							while (promenna!=INPUT && hr->hradlo[i]->vnejsi[vynasobeno[i]]->vnitrni2[promenna]==0) promenna++;
						}
						else {
							nove->vnejsi[novy_soucin]->vnitrni2[promenna] = hr->hradlo[i]->vnejsi[vynasobeno[i]]->vnitrni2[promenna];  // vlozim
#ifndef NSTEJNA
							stejny_clen[i] = false;
#endif
							promenna++;
							while (promenna!=INPUT && hr->hradlo[i]->vnejsi[vynasobeno[i]]->vnitrni2[promenna]==0) promenna++;
						}
				}
			}

			if (!zrus) {
				absorpce(nove,&novy_soucin);
				novy_soucin++;  // bude novy nasobeny clen
				if (novy_soucin == nove->clenu-1) nove->Zvetsi();  // pokud nedostacuje pole pro nove souciny, tak se zvetsi
				i=poc_vst_int-1;
			}
			else i--;
			while (hr->hradlo[0]->vnejsi[vynasobeno[0]] != NULL) {  // v poli vynasobeno[] si pamatuju ktere cleny sem spolu uz vynasobil (ve stylu binarniho cisla s vic moznostmi nez 0/1) a postupne ho zvetsuju
#ifndef NSTEJNA
				if (stejny_clen[i]) {  // pokud davam stejny clen
					j=vynasobeno[i];
					while (hr->hradlo[i]->vnejsi[j] != NULL) j++;
					vynasobeno[i] = j;  // posunu se na konec
					stejny_clen[i] = false;
				}
				else 
#endif
					vynasobeno[i]++;
				if (i>0 && hr->hradlo[i]->vnejsi[vynasobeno[i]] == NULL) vynasobeno[i--]=0;
				else break;
			}
		}
	return nove;
}

TVnejsi* Collapsing::my_or(THradlo* hr) {  // (ab+cd) OR (ef+gh) = ab + cd + ef +gh
		TVnejsi* nove = new TVnejsi;
		int i,j,k,poc_vst_int=hr->poc_vst;
		int novy_soucin = 0;
		for (i=0; i<poc_vst_int; i++) {
				j=0;
				while (hr->hradlo[i]->vnejsi[j] != NULL) { 
					nove->vnejsi[novy_soucin] = new TVnitrni(INPUT);
					for (k=0; k<INPUT; k++) nove->vnejsi[novy_soucin]->vnitrni2[k] = hr->hradlo[i]->vnejsi[j]->vnitrni2[k];
					absorpce(nove,&novy_soucin);
					novy_soucin++; j++;
					if (novy_soucin == nove->clenu-1) nove->Zvetsi();  // pokud nedostacuje pole pro nove souciny, tak se zvetsi
				}
			}
	return nove;
}

TVnejsi* Collapsing::my_nor(THradlo* hr) {  // (ab+cd) NOR (ef+gh) = !a!c!e!g + !a!c!e!h + ... 16 soucinu
		TVnejsi* nove = new TVnejsi;
		int i,j,k,poc_vst_int=hr->poc_vst;
		bool zrus;
		for (i=0,k=0; i<poc_vst_int; i++)  k += hr->hradlo[i]->clenu;  // zjisti max. pocet vsech nasobenych soucinu
		int *vynasobeno = new int[k];  // pole prave nasobenych clenu
#ifndef NSTEJNA
		bool *stejna_promenna = new bool[k];  // pole urcujici zda promenna tam uz je, pokud true, tak se nepude na dalsi promennou v clenu ale na konec
		for (j=0; j<k; j++) stejna_promenna[j]=false;
#endif
		for (i=0,k=0; i<poc_vst_int; i++) {  // nastavi ve vsech soucinech iterator na prvni promennou
			j=0;
			while (hr->hradlo[i]->vnejsi[j] != NULL) {
				vynasobeno[k] = 0;  //hr->hradlo[i]->vnejsi[j]->vnitrni3.begin();
				j++; k++;
			}
		}
		int novy_soucin=0;
#ifndef NSTEJNA
		int opet_promenna;
#endif
		while (vynasobeno[0] != INPUT) {  // dokud je v prvnim nasobenem clenu este co nasobit
			nove->vnejsi[novy_soucin] = new TVnitrni(INPUT);
			zrus = false;
			k=0;
			
			for (i=0; i<poc_vst_int && !zrus; i++) {  // tolikrat se provede nasobeni jednotlivych vstupu
				j=0;
				while (hr->hradlo[i]->vnejsi[j] != NULL && !zrus) {  // tolik clenu se este vzdy nasobi
					if (vynasobeno[k] == 0) {  // bude se to delat jen kdyz je to prvni promenna
#ifndef NSTEJNA
						opet_promenna = 0;  // nastavim se na zacatek celnu,ze ktereho chcu pridavat promennou a pak ho projdu,ci nektera promenna uz neni v novem, a kdyz jo,tak ostatni promenne nema smysl prochazet
						while (opet_promenna != INPUT && !(nove->vnejsi[novy_soucin]->vnitrni2[opet_promenna] != 0 && nove->vnejsi[novy_soucin]->vnitrni2[opet_promenna] == -hr->hradlo[i]->vnejsi[j]->vnitrni2[opet_promenna])) opet_promenna++;
						if (opet_promenna != INPUT) vynasobeno[k] = opet_promenna;
						else 
#endif
							while (hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[k]] == 0) vynasobeno[k]++;
					}
					if (nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[k]] == hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[k]]) {  // mam dat negovanou promennou, ale pokud uz tam je ta neznegovana, tak je vysledek logicka 0
						delete nove->vnejsi[novy_soucin];
						nove->vnejsi[novy_soucin]=NULL;
						zrus = true;
					}
					else { // vlozim
						if (nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[k]] == -hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[k]]) {  // pokud uz tam promenna je, tak az mam prejit v danem soucinu na dalsi promennou tak prejdu na konec
#ifndef NSTEJNA
							stejna_promenna[k] = true;
#endif
							j++;k++;
						}
						else {
							nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[k]] = -hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[k]];
							j++; k++;
						}
					}
				}
			}

			if (!zrus) {
				absorpce(nove,&novy_soucin);
				novy_soucin++;  // bude novy soucin
				if (novy_soucin == nove->clenu-1) nove->Zvetsi();  // pokud nedostacuje pole pro nove souciny, tak se zvetsi
				i=poc_vst_int-1; j--; k--;
			}
			else i--;

			while (vynasobeno[0] != INPUT) {  // v poli vynasobeno[] si pamatuju ktere cleny sem spolu uz vynasobil (ve stylu binarniho cisla s vic moznostmi nez 0/1) a postupne ho zvetsuju
#ifndef NSTEJNA
				if (stejna_promenna[k]) {  // pokud davam stejnou promennou
					vynasobeno[k] = INPUT;  // posunu se na konec
					stejna_promenna[k] = false;
				}
				else 
#endif
					while (vynasobeno[k]++, vynasobeno[k]!=INPUT && hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[k]] == 0) ;
				if (k>0 && vynasobeno[k] == INPUT) {
					vynasobeno[k--] = 0;
					if (j>0) j--;
					else {
						i--;
						while (hr->hradlo[i]->vnejsi[j] !=NULL) j++;
						j--;
					}
				}
				else break;
			}
		}
	return nove;
}

TVnejsi* Collapsing::my_nand(THradlo* hr) {  // (ab+cd) NAND (ef+gh) = !a!c + !a!d + !b!c + !b!d + !e!g + !e!h + !f!g + !f!h
		TVnejsi* nove = new TVnejsi;
		int i,j,poc_vst_int=hr->poc_vst;
		bool zrus;
		int novy_soucin=0;
#ifndef NSTEJNA
		int opet_promenna;
#endif
		for (i=0; i<poc_vst_int; i++) {  // tolikrat se provede nasobeni jednotlivych vstupu
			int *vynasobeno = new int[hr->hradlo[i]->clenu];  // pole prave nasobenych clenu
#ifndef NSTEJNA
			bool *stejna_promenna = new bool[hr->hradlo[i]->clenu];  // pole urcujici zda promenna tam uz je, pokud true, tak se nepude na dalsi promennou v clenu ale na konec
			for (j=0; j<hr->hradlo[i]->clenu; j++) stejna_promenna[j]=false;
#endif
			j=0;
			while (hr->hradlo[i]->vnejsi[j] != NULL) {  // nastavi ve vsech soucinech iterator na prvni promennou
				vynasobeno[j] = 0;
				j++;
			}
			while (vynasobeno[0] != INPUT) {  // dokud je v prvnim nasobenem clenu este co nasobit
				nove->vnejsi[novy_soucin] = new TVnitrni(INPUT);
				zrus = false;
				j=0;
				while (hr->hradlo[i]->vnejsi[j] != NULL && !zrus) {  // tolik clenu se vzdy vynasobi
					if (vynasobeno[j] == 0) {  // bude se to delat jen kdyz je to prvni promenna
#ifndef NSTEJNA
						opet_promenna = 0;  // nastavim se na zacatek celnu,ze ktereho chcu pridavat promennou a pak ho projdu,ci nektera promenna uz neni v novem, a kdyz jo,tak ostatni promenne nema smysl prochazet
						while (opet_promenna != INPUT && !(nove->vnejsi[novy_soucin]->vnitrni2[opet_promenna] != 0 && nove->vnejsi[novy_soucin]->vnitrni2[opet_promenna] == -hr->hradlo[i]->vnejsi[j]->vnitrni2[opet_promenna])) opet_promenna++;
						if (opet_promenna != INPUT) vynasobeno[j] = opet_promenna;
						else 
#endif
							while (hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[j]] == 0) vynasobeno[j]++;
					}
					if (nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[j]] == hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[j]]) {  // mam dat negovanou promennou, ale pokud uz tam je ta neznegovana, tak je vysledek logicka 0
						delete nove->vnejsi[novy_soucin];
						nove->vnejsi[novy_soucin]=NULL;
						zrus = true;
					}
					else { // vlozim
						if (nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[j]] == -hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[j]]) {  // pokud uz tam promenna je, tak az mam prejit v danem soucinu na dalsi promennou tak prejdu na konec
#ifndef NSTEJNA
							stejna_promenna[j] = true;
#endif
							j++;
						}
						else {
							nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[j]] = -hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[j]];
							j++;
						}
					}
				}
				if (!zrus) {
					absorpce(nove,&novy_soucin);
					novy_soucin++;  // bude novy nasobeny clen
					if (novy_soucin == nove->clenu-1) nove->Zvetsi();  // pokud nedostacuje pole pro nove souciny, tak se zvetsi
					j--;
				}
				while (vynasobeno[0] != INPUT) {  // v poli vynasobeno[] si pamatuju ktere cleny sem spolu uz vynasobil (ve stylu binarniho cisla s vic moznostmi nez 0/1) a postupne ho zvetsuju
#ifndef NSTEJNA
					if (stejna_promenna[j]) {  // pokud davam stejnou promennou
						vynasobeno[j] = INPUT;  // posunu se na konec
						stejna_promenna[j] = false;
					}
					else 
#endif
						while (vynasobeno[j]++, vynasobeno[j]!=INPUT && hr->hradlo[i]->vnejsi[j]->vnitrni2[vynasobeno[j]] == 0) ;
					if (j>0 && vynasobeno[j] == INPUT) vynasobeno[j--] = 0;
					else break;
				}
			}
		} 
	return nove;
}

TVnejsi* Collapsing::my_not(TVnejsi* vn) {  // NOT(ab+cd) = !a!c! + !a!d + !b!c + !b!d
		TVnejsi* nove = new TVnejsi;
		int i,j,k;
		bool zrus;
			k = vn->clenu;  // zjisti max. pocet vsech nasobenych soucinu
			int *vynasobeno = new int[k];  // pole prave nasobenych clenu
#ifndef NSTEJNA
			bool *stejna_promenna = new bool[k];  // pole urcujici zda promenna tam uz je, pokud true, tak se nepude na dalsi promennou v clenu ale na konec
#endif
			for (j=0; j<k; j++) {
#ifndef NSTEJNA
				stejna_promenna[j]=false;
#endif
				vynasobeno[j] = 0;  // nastavi ve vsech soucinech iterator na prvni promennou
			}
			int novy_soucin=0;
#ifndef NSTEJNA
			int opet_promenna;
#endif
			while (vynasobeno[0] != INPUT) {  // dokud je v prvnim nasobenem clenu este co nasobit
				nove->vnejsi[novy_soucin] = new TVnitrni(INPUT);
				zrus = false;
				k=0;
				for (i=0; i<1 && !zrus; i++) {  // tolikrat se provede nasobeni jednotlivych vstupu
					j=0;
					while (vn->vnejsi[j] != NULL && !zrus) {  // tolik clenu se este vzdy nasobi
						if (vynasobeno[k] == 0) {  // bude se to delat jen kdyz je to prvni promenna
#ifndef NSTEJNA
							opet_promenna = 0;  // nastavim se na zacatek celnu,ze ktereho chcu pridavat promennou a pak ho projdu,ci nektera promenna uz neni v novem, a kdyz jo,tak ostatni promenne nema smysl prochazet
							while (opet_promenna != INPUT && !(nove->vnejsi[novy_soucin]->vnitrni2[opet_promenna] != 0 && nove->vnejsi[novy_soucin]->vnitrni2[opet_promenna] == -vn->vnejsi[j]->vnitrni2[opet_promenna])) opet_promenna++;
							if (opet_promenna != INPUT) vynasobeno[k] = opet_promenna;
							else 
#endif
								while (vn->vnejsi[j]->vnitrni2[vynasobeno[k]] == 0) vynasobeno[k]++;
						}
						if (nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[k]] == vn->vnejsi[j]->vnitrni2[vynasobeno[k]]) {  // mam dat negovanou promennou, ale pokud uz tam je ta neznegovana, tak je vysledek logicka 0
							delete nove->vnejsi[novy_soucin];
							nove->vnejsi[novy_soucin]=NULL;
							zrus = true;
						}
						else { // vlozim
							if (nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[k]] == -vn->vnejsi[j]->vnitrni2[vynasobeno[k]]) {  // pokud uz tam promenna je, tak az mam prejit v danem soucinu na dalsi promennou tak prejdu na konec
#ifndef NSTEJNA
								stejna_promenna[k] = true;
#endif
								j++;k++;
							}
							else {
								nove->vnejsi[novy_soucin]->vnitrni2[vynasobeno[k]] = -vn->vnejsi[j]->vnitrni2[vynasobeno[k]];
								j++; k++;
							}
						}
					}
				}

				if (!zrus) {
					absorpce(nove,&novy_soucin);
					novy_soucin++;  // bude novy soucin
					if (novy_soucin == nove->clenu-1) nove->Zvetsi();  // pokud nedostacuje pole pro nove souciny, tak se zvetsi
					i=0; j--; k--;
				}

				while (vynasobeno[0] != INPUT) {  // v poli vynasobeno[] si pamatuju ktere cleny sem spolu uz vynasobil (ve stylu binarniho cisla s vic moznostmi nez 0/1) a postupne ho zvetsuju
#ifndef NSTEJNA
					if (stejna_promenna[k]) {  // pokud davam stejnou promennou
						vynasobeno[k] = INPUT;  // posunu se na konec
						stejna_promenna[k] = false;
					}
					else 
#endif
						while (vynasobeno[k]++, vynasobeno[k]!=INPUT && vn->vnejsi[j]->vnitrni2[vynasobeno[k]] == 0) ;
					if (k>0 && vynasobeno[k] == INPUT) {
						vynasobeno[k--] = 0;
						if (j>0) j--;
					}
					else break;
				}
			}
	return nove;
}



TVnejsi* Collapsing::minim_hradlo(std::string hradlo, StructArch* sa, bool negace) {
	int i,j,k;
	Instance *instance,*instpom;
	StructArch :: NetIter ni;
	Net :: JoinIter ji;
	Port <Instance>* iport;
#ifdef DEBUG
	clock_t s;
#endif

	instance = sa->FindInstance(hradlo);
	std::string typ_hradla = (instance->entity())->name();
	std::string nazev_hradla, poc_vst, vstup, vstup_pom;
	int poc_vst_int;
	for (i=0; i<typ_hradla.size(); i++) {  //zjistim nazev hradla a pocet vstupu
		if(typ_hradla[i]>'0' && typ_hradla[i]<='9') {
			nazev_hradla = typ_hradla.substr(0,i);
			poc_vst = typ_hradla.substr(i,typ_hradla.length()-i);
			poc_vst_int = typ_hradla[i]-'0';
		}
	}

	THradlo *hr = new THradlo(poc_vst_int);  // struktura pro ulozeni 2 urovnove minimalizovaneho obvodu
	TVnejsi *nove;  // zde bude nove vysledne minimalizovane hradlo

	int pocet_hradel=0;  // pocet hradel na ktere jde vystup
#ifndef NOPAK_HR
	if (!negace) {
	map <std::string,THotove_hradlo> :: iterator uk;
//				if (pocet_hradel > 1 && !negace) {  // tak si muzu hradlo uchovat nebo ho vybrat uz hotove
	if ((uk = seznam_hradel.find(hradlo)) ==  seznam_hradel.end()) {
						//vlozim do seznamu a nastavim pocet
						//pocet_hradel = pocet_vstupu;
		vstup = "O."+hradlo;
		bool nalezeno = false;
		for (ni = sa->NetBeg(); (!nalezeno) && ni != sa->NetEnd(); ni++) {
			pocet_hradel = 0;
			for (ji = ni->second->JoinBeg(); ji != ni->second->JoinEnd(); ji++) {
				vstup_pom = (*ji)->name();
				iport = dynamic_cast <Port <Instance>* > (*ji);
				if (iport) {vstup_pom += "." + iport->Parent()->name(); pocet_hradel++;}
				if (vstup == vstup_pom) {nalezeno = true; pocet_hradel--;}
			}
		}
	}
	else {
		k = 0;
		nove = new TVnejsi(uk->second.hradlo->clenu);
		while (uk->second.hradlo->vnejsi[k]!=NULL) {
			nove->vnejsi[k] = new TVnitrni(INPUT);
			for (j=0; j<INPUT; j++) nove->vnejsi[k]->vnitrni2[j] = uk->second.hradlo->vnejsi[k]->vnitrni2[j];
			k++;
		}
//		if ((--(uk->second.pocet)) == 0) seznam_hradel.erase(uk);
		delete hr;
		return nove;
	}
	}
#endif
	for (i=0; i<poc_vst_int; i++) {
		vstup = "I"; vstup += i+'1';  // pokud min vstupu nez 2 (NOT) tak se necisluje
		vstup += "."+hradlo;
		for (ni = sa->NetBeg(); ni != sa->NetEnd(); ni++) {
			for (ji = ni->second->JoinBeg(); ji != ni->second->JoinEnd(); ji++) {
				vstup_pom = (*ji)->name();
				iport = dynamic_cast <Port <Instance>* > (*ji);
				if (iport) vstup_pom += "." + iport->Parent()->name() ;
				if (vstup == vstup_pom) break;
			}
			if (vstup == vstup_pom) {  //nasel sem net na dalsi hradlo
				vstup = vstup_pom;
				std::string vystup;
/*				int pocet_vstupu = 0;
				for (ji = ni->second->JoinBeg(); ji != ni->second->JoinEnd(); ji++) {  //zjistim pocet vstupu
					vystup = (*ji)->name();
					iport = dynamic_cast <Port <Instance>* > (*ji);
					if (iport)
						if (vystup[0] == 'I') pocet_vstupu++;  // vstup
				}
				map <std::string,THotove_hradlo> :: iterator uk;
				if (pocet_vstupu > 1 && !negace) {  // tak si muzu hradlo uchovat nebo ho vybrat uz hotove
					if ((uk = seznam_hradel.find(hradlo)) ==  seznam_hradel.end()) {
						//vlozim do seznamu a nastavim pocet
						pocet_hradel = pocet_vstupu;
					}
					else {
						k = 0;
						nove = new TVnejsi(uk->second.hradlo->clenu);
						while (uk->second.hradlo->vnejsi[k]!=NULL) {
							nove->vnejsi[k] = new TVnitrni(INPUT);
							for (j=0; j<INPUT; j++) nove->vnejsi[k]->vnitrni2[j] = uk->second.hradlo->vnejsi[k]->vnitrni2[j];
							k++;
						}
						if ((--(uk->second.pocet)) == 0) seznam_hradel.erase(uk);
						delete hr;
						return minim(nove);
					}
				}*/
				for (ji = ni->second->JoinBeg(); ji != ni->second->JoinEnd(); ji++) {  //zjistim na jaky vystup de
					vystup = (*ji)->name();
					iport = dynamic_cast <Port <Instance>* > (*ji);
					if (iport) {
						if (vystup[0] == 'O') {		// hledany vystup
#ifndef NNOT
							if (nazev_hradla=="not" && !negace) {  // pokud dalsi hradlo bude negovane,tak zrusim not a z negovaneho hradla udelam normalni
								instpom = sa->FindInstance(iport->Parent()->name());
								std::string typ_hradla_pom = (instpom->entity())->name();
								std::string nazev_hradla_pom;
								for (j=0; j<typ_hradla_pom.size(); j++) {  //zjistim nazev hradla
									if(typ_hradla_pom[j]>'0' && typ_hradla_pom[j]<='9') {
										nazev_hradla_pom = typ_hradla_pom.substr(0,j);
									}
								}
								if (nazev_hradla_pom == "not" || nazev_hradla_pom == "nor" || nazev_hradla_pom == "nand" || nazev_hradla_pom == "xnor") {
									nove = minim_hradlo(iport->Parent()->name(),sa,true);
									delete hr;
									if (pocet_hradel>1) {  // vystup se opakuje v obvodu vicekrat, proto si ho ulozime
										nove = minim(nove);
										k = 0;
										THotove_hradlo h;
										h.pocet = pocet_hradel-1;  // nebot to jedno uz pouziju ted
										h.hradlo = new TVnejsi(nove->clenu);
										while (nove->vnejsi[k]!=NULL) {
											h.hradlo->vnejsi[k] = new TVnitrni(INPUT);
											for (j=0; j<INPUT; j++) h.hradlo->vnejsi[k]->vnitrni2[j] = nove->vnejsi[k]->vnitrni2[j];
											k++;
										}
										seznam_hradel.insert(std::make_pair(std::string(hradlo),h));
									}
									return nove;
								}
								else hr->hradlo[i] = minim_hradlo(iport->Parent()->name(),sa);
							}
							else 
#endif
								hr->hradlo[i] = minim_hradlo(iport->Parent()->name(),sa); 
						}
					}
					else {  // je to vstupni port celeho obvodu
						hr->hradlo[i]=new TVnejsi;
						hr->hradlo[i]->vnejsi[0]=new TVnitrni(INPUT);
						j=0;
						while (vystup != vstupy[j]) j++;
						hr->hradlo[i]->vnejsi[0]->vnitrni2[j] = 1; 
						break;
					}
				}
				break;
			}
		}
	}
	

	if (negace)
		if (nazev_hradla == "nand") nazev_hradla = "and";
		else if (nazev_hradla == "nor") nazev_hradla = "or";
		else if (nazev_hradla == "xnor") nazev_hradla = "xor";
		else if (nazev_hradla == "not") nazev_hradla = "buf";

	if (nazev_hradla == "buf") return minim(hr->hradlo[0]);
	else if (nazev_hradla == "and") {
#ifdef DEBUG
		s=clock();
#endif
		nove = my_and(hr);
#ifdef DEBUG
		casAnd += clock() - s;
		pocAnd++;
#endif
	}

	else if (nazev_hradla == "nand") {
#ifdef DEBUG
		s=clock();
#endif
		nove = my_nand(hr);
#ifdef DEBUG
		casNand += clock() - s;
		pocNand++;
#endif
	}
	else if (nazev_hradla == "or") {
#ifdef DEBUG
		s=clock();
#endif
		nove = my_or(hr);
#ifdef DEBUG
		casOr += clock() - s;
		pocOr++;
#endif
	}
	else if (nazev_hradla == "nor") {
#ifdef DEBUG
		s=clock();
#endif
		nove = my_nor(hr);
#ifdef DEBUG
		casNor += clock() - s;
		pocNor++;
#endif
	}
	else if (nazev_hradla == "not") {
#ifdef DEBUG
		s=clock();
#endif
		nove = my_not(hr->hradlo[0]);
#ifdef DEBUG
		casNot += clock() - s;
		pocNot++;
#endif
	}
	else if (nazev_hradla == "xnor") {  // OR(AND(a,b,c,..),AND(!a,!b,!c,...))
#ifdef DEBUG
		s=clock();
#endif
		THradlo *hr2 = new THradlo(2);  // struktura pro pripraveni na OR
		THradlo *noty = new THradlo(poc_vst_int);  // struktura pro ulozeni !a !b !c ...
		for (i=0; i<poc_vst_int; i++) noty->hradlo[i] = my_not(hr->hradlo[i]);
		hr2->hradlo[0] = my_and(hr);
		hr2->hradlo[1] = my_and(noty);
		nove = my_or(hr2);
		delete hr2;
		delete noty;
#ifdef DEBUG
		casXnor += clock() - s;
		pocXnor++;
#endif
	}
	else if (nazev_hradla == "xor") {  // AND(OR(a,b,c,..),OR(!a,!b,!c,...))
#ifdef DEBUG
		s=clock();
#endif
		THradlo *hr2 = new THradlo(2);  // struktura pro pripraveni na AND
		THradlo *noty = new THradlo(poc_vst_int);  // struktura pro ulozeni !a !b !c ...
		for (i=0; i<poc_vst_int; i++) noty->hradlo[i] = my_not(hr->hradlo[i]);
		hr2->hradlo[0] = my_or(hr);
		hr2->hradlo[1] = my_or(noty);
		nove = my_and(hr2);
		delete hr2;
		delete noty;
#ifdef DEBUG
		casXor += clock() - s;
		pocXor++;
#endif
	}
//	delete hr;
#ifndef NOPAK_HR
	if (pocet_hradel>1) {
		nove = minim(nove);
		k = 0;
		THotove_hradlo h;
		h.pocet = pocet_hradel-1;  // nebot to jedno pouziju uz ted
		h.hradlo = new TVnejsi(nove->clenu);
		while (nove->vnejsi[k]!=NULL) {
			h.hradlo->vnejsi[k] = new TVnitrni(INPUT);
			for (j=0; j<INPUT; j++) h.hradlo->vnejsi[k]->vnitrni2[j] = nove->vnejsi[k]->vnitrni2[j];
			k++;
		}
		seznam_hradel.insert(std::make_pair(std::string(hradlo),h));
		return nove;
	}
#endif
	return minim(nove);
}

void Collapsing::minimalize(hub::Hub theHub) {	
#if defined DEBUG || defined CAS
	clock_t s = clock(),e;
#endif
	Hub :: LibIter li;
	hub::Entity* ent;
	PortProto* port;

	int i,j,k,l;
	li = theHub.LibBeg(); li++;
	ent = li->second->EntBeg()->second;
	StructArch* sa = ent->ArchBeg()->second->Impl <StructArch> ();

	std::string nazev_hradla;
	std::string inst, inst2, inst_pom, naz;

	for (i=0,j=0; i<ent->PortNum(); i++) {				// prohledam porty
		port = ent->FindPort(i,PortProto::outDir);		// najdu vystupni port
		if (!port) break;		// neni vystupni port
		j++;
	}
	THradlo *hr = new THradlo(j);
	vstupy = new string[ent->PortNum()-j];
	j=0;
	for (i=0; i<ent->PortNum(); i++) {				// prohledam porty
		port = ent->FindPort(i,PortProto::inDir);		// najdu vstupni port
		if (!port) break;		// neni vstupni port
		vstupy[j++] = port->name();
	}
	INPUT = j;

#ifdef DEBUG
	casAnd=0,casNand=0,casOr=0,casNor=0,casNot=0,casXor=0,casXnor=0,casEsp=0,casAbs=0;
	pocAnd=0,pocNand=0,pocOr=0,pocNor=0,pocNot=0,pocXor=0,pocXnor=0,pocEsp=0,pocAbs=0;
#endif

	char *pomc = arg[2];
	for (i=0; i<ent->PortNum(); i++) {				// prohledam porty
		port = ent->FindPort(i,PortProto::outDir);		// najdu vystupni port
		if (!port) break;		// neni dalsi vystupni port
		hr->hradlo[i]=new TVnejsi;
#ifdef STATE
		printf("\r%d/%d",i,hr->poc_vst);
#endif
		hr->hradlo[i] = minim_hradlo(port->name(),sa);  // zde budou minimalizovane vystupni porty
		if (strcmp(arg[3],"true") == 0 && atoi(pomc) != 1) {  // kdyz mam pouzit espresso na vysledek && pokud to uz neni zbytecne
			arg[2] = "1";
			hr->hradlo[i] = minim(hr->hradlo[i]);
			arg[2] = pomc;
		}
	}
#ifdef STATE
		printf("\r%d/%d\n",hr->poc_vst,hr->poc_vst);
#endif
#if defined DEBUG || defined CAS
	e = clock();
#endif
	k=0;
	for (l=0; l<i; l++) {
		j=0;
		while (hr->hradlo[l]->vnejsi[j] != NULL) j++;
		k+=j;
	}
#ifdef DEBUG
	printf("AND: %d/%d, NAND %d/%d, OR: %d/%d, NOR: %d/%d, NOT: %d/%d, XOR: %d/%d, XNOR: %d/%d\n",casAnd,pocAnd,casNand,pocNand,casOr,pocOr,casNor,pocNor,casNot,pocNot,casXor,pocXor,casXnor,pocXnor);
	printf("Espresso: %d/%d, Absorpce: %d/%d, Ostatni: %d\n",casEsp,pocEsp,casAbs,pocAbs,e-s-casEsp-casAnd-casNand-casOr-casNor-casNot-casXor-casXnor);
	printf("Termu: %d\n",k);
#endif
#if defined DEBUG || defined CAS
	printf("Cas: %d\n",e-s);
#endif

//	int pole[54] = {0,8,9,10,11,12,13,14,15,16,17,1,18,19,20,21,22,23,24,25,26,27,2,28,29,30,31,32,33,34,3,4,5,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,6,51,52,53,7};
	FILE *soubor = fopen(arg[1],"w");//(char*)(((std::string)arg[1]).substr(0,((std::string)arg[1]).find_first_of('.')) + ".pla"),"w");
	fprintf(soubor,".i %d\n",INPUT);
	fprintf(soubor,".o %d\n",hr->poc_vst);
	fprintf(soubor,".ilb");
	for (i = 0; i < INPUT; i++)  // prohledam porty
		fprintf(soubor," %s",vstupy[i].data());
//		fprintf(soubor," %s",vstupy[pole[i]].data());
	fprintf(soubor,"\n.ob");
	for (i=0; i<ent->PortNum(); i++) {				// prohledam porty
		port = ent->FindPort(i,PortProto::outDir);		// najdu vystupni port
		if (!port) break;		// neni vystupni port
		fprintf(soubor," %s",port->name().data());//vstupy[i]);
	}
	fprintf(soubor,"\n.p %d\n",k);
	//l=6;
	for (i=0; i<hr->poc_vst; i++) {
		j=0;
		while (hr->hradlo[i]->vnejsi[j] != NULL) {
			for (k=0; k<INPUT; k++)
//				switch (hr->hradlo[i]->vnejsi[j]->vnitrni2[pole[k]]) {
				switch (hr->hradlo[i]->vnejsi[j]->vnitrni2[k]) {
					case 0: fprintf(soubor,"-"); break;
					case 1: fprintf(soubor,"1"); break;
					case -1: fprintf(soubor,"0"); break;
				}
			fprintf(soubor," ");
			for (k=0; k<hr->poc_vst; k++)
				if (k!=i) fprintf(soubor,"0");
				else fprintf(soubor,"1");
			fprintf(soubor,"\n");
			j++;
		}
	}
	fprintf(soubor,".e\n");
	fclose(soubor);

}