#include "Decomposer.h"

#ifdef WIN32
#include <windows.h>
#undef TRUE
#undef FALSE
#endif

const char* gDashes="------------------------------------------------------------\n";

bool CubeComp::operator()(const Cube& x,const Cube& y) const
{
	//Komparator krychli

	if (x.size()!=y.size()) return x.size()<y.size();

	for (Cube::const_iterator iX=x.begin(),iY=y.begin();iX!=x.end() && iY!=y.end();iX++,iY++)
		if (*iX!=*iY) return *iX<*iY;

	return false;
}

bool KernelComp::operator()(const Kernel& x,const Kernel& y) const
{
	//Komparator kernelu

	if (x.size()!=y.size()) return x.size()<y.size();

	for (Kernel::const_iterator iX=x.begin(),iY=y.begin();iX!=x.end() && iY!=y.end();iX++,iY++)
	{
		if (iX->size()!=iY->size()) return iX->size()<iY->size();

		for (Cube::const_iterator iiX=iX->begin(),iiY=iY->begin();iiX!=iX->end() && iiY!=iY->end();iiX++,iiY++)
			if (*iiX!=*iiY) return *iiX<*iiY;
	}

	return false;
}

bool KernelLiteralComp::operator()(const Kernel& x,const Kernel& y) const
{
	//Komparator kernelu dle poctu literalu

	int sizeX=0;
	int sizeY=0;

	for (Kernel::const_iterator cube=x.begin();cube!=x.end();cube++)
		sizeX+=cube->size();

	for (Kernel::const_iterator cube=y.begin();cube!=y.end();cube++)
		sizeY+=cube->size();

	return (sizeX==sizeY)?x>y:sizeX>sizeY;
}

KernelValue::KernelValue()
{
	function=NULL;
	gainCache=0xf0000000;
}

int Decomposer::connect(Gate* g,hub::Net** net)
{
	//Pripojeni hradla do site, vrati vystupni spoj celeho hradla

	if (g->net)
	{
		(*net)=g->net;
		return 0;
	}

	static int netNumber=0;
	static int gateNumber=0;
	static char buff[4];

	int ret=-1;

	switch (g->type)
	{
	case PORT:
		{
			//Pokud je hradlo port, pripojime ho primo
			hub::Port<hub::StructArch>* port=strArch->FindPort(g->portNumber-1,hub::PortProto::inDir);
			(*net)=port->Connected();
			g->net=(*net);
		}
		return 0;
	case INVPORT:
		{
			//Pokud je hradlo invport, podivame se, jestli uz neni v cache
			hub::Port<hub::Instance>* portInst=invPorts[g->portNumber-1];
			if (!portInst)
			{
				//Pokud neni, vytvorime invertor a pripojime port na nej; pridame do cache
				hub::Entity* invEnt=getGateEntity(INV,1);
				std::string instName("INV");
				hub::Instance* inst=invEnt->Instantiate(instName+"_"+itoa(gateNumber++,buff,10),strArch,invEnt->CurrentArch());
				hub::Port<hub::StructArch>* port=strArch->FindPort(g->portNumber-1,hub::PortProto::inDir);
				hub::Net* portNet=port->Connected();
				portInst=inst->FindPort(0,hub::PortProto::inDir);
				portNet->Join(portInst);
				portInst=inst->FindPort(0,hub::PortProto::outDir);
				invPorts[g->portNumber-1]=portInst;
				portNet=strArch->FindOrCreateNet(std::string("inv_i")+itoa(g->portNumber-1,buff,10));
				portNet->Join(portInst);
			}
			(*net)=portInst->Connected();
			g->net=(*net);
		}
		return 0;
	case FALSE:
	case TRUE:
	case BUF:
	case INV:
	case AND:
	case OR:
	case XOR:
	case NAND:
	case NOR:
	case XNOR:
		{
			//Pro jine hradlo pridame konkretni reprezentaci
			hub::Entity* gateEntity=getGateEntity(g);
			std::string instName;
			Gate::getGateName(g->type,g->inputs.size(),instName);
			hub::Instance* inst=gateEntity->Instantiate(instName+"_"+itoa(gateNumber++,buff,10),strArch,gateEntity->CurrentArch());
			hub::Port<hub::Instance>* port=inst->FindPort(0,hub::PortProto::outDir);
			(*net)=strArch->FindOrCreateNet(std::string("net_")+itoa(netNumber++,buff,10));
			(*net)->Join(port);
			int p=0;
			for (Gates::iterator iG=g->inputs.begin();iG!=g->inputs.end();iG++)
			{
				port=inst->FindPort(p++,hub::PortProto::inDir);
				hub::Net* pNet;
				if (ret=connect(*iG,&pNet)) return ret;
				pNet->Join(port);
			}
			g->net=(*net);
		}
		return 0;
	}
	return -1;
}

hub::Entity* Decomposer::getGateEntity(Gate* g)
{
	return getGateEntity(g->type,g->inputs.size());
}

hub::Entity* Decomposer::getGateEntity(GateType type,int inPortCount)
{
	static char buff[4];

	//Vytvorime agenta pro vyhledavani v knihovne dle chovani
	hub::SymFuncAgent* agent=new hub::SymFuncAgent(getHubFunc(type),inPortCount);
	hub::Architecture* gateArch=libGates->FindArch(*agent,"");
	hub::BehavArch* gateArchImpl;
	hub::Entity* gateEntity;
	if (gateArch)
		gateEntity=gateArch->Parent();
	else
	{
		//Pokud v knihovne ekvivalentni funkce neexistuje, vytvorime ji
		std::string entityName;
		Gate::getGateName(type,inPortCount,entityName);
		gateEntity=libGates->CreateUniqueEntity(entityName);
		gateArch=gateEntity->CreateArch<hub::BehavArch>("");
		gateEntity->CurrentArch(gateArch);
		gateArchImpl=dynamic_cast<hub::BehavArch*>(gateArch->Impl());
		gateArchImpl->description(agent->CreateDescr());

		std::string portName="I";

		gateEntity->CreateNextPort("O",hub::PortProto::outDir);
		for (int i=1;i<=inPortCount;i++)
			gateEntity->CreateNextPort(portName+itoa(i,buff,10),hub::PortProto::inDir);
	}

	delete agent;

	return gateEntity;
}

Gate* Decomposer::buildGateNetFromPLA(hub::PlaDescr* pla,int outPortNumber)
{
	//Vytvorime sit hradel Gate ze zadaneho pla
	Gate* ret=new Gate(OR);
	Gate* and=NULL;

	hub::PlaDescr::MatrixIter in;
	hub::PlaDescr::MatrixIter out;
	for (in=pla->inpBeg(),out=pla->outpBeg();in!=pla->inpEnd();in++,out++)
		if ((*out)[outPortNumber]=='1' || (*out)[outPortNumber]=='4')
		{
			and=new Gate(AND);
			for (int i=0;i<pla->inputNo();i++)
				if ((*in)[i]=='1' || (*in)[i]=='4')
					and->addInput(new Gate(PORT,i+1));
				else if ((*in)[i]=='0')
					and->addInput(new Gate(INVPORT,i+1));

			if (and->inputs.empty())
			{
				delete and;
				and=NULL;
			}
			//else if (and->inputs.size()==1)
			//{
			//	Gate* temp=and;
			//	and=(*and->inputs.begin());
			//	delete temp;
			//}

			if (and) ret->addInput(and);
		}

		if (ret->inputs.empty())
		{
			ret->type=FALSE;
		}
		//else if (ret->inputs.size()==1)
		//{
		//	Gate* temp=ret;
		//	ret=(*ret->inputs.begin());
		//	delete temp;
		//}

		return ret;
}

bool Decomposer::verify(Gate* gate,hub::PlaDescr* pla,int outPortNumber)
{
	//Overeni zadane funkce
	hub::PlaDescr::MatrixIter in;
	hub::PlaDescr::MatrixIter out;
	for (in=pla->inpBeg(),out=pla->outpBeg();in!=pla->inpEnd();in++,out++)
	{
		bool ret=gate->process((*in).c_str());
		bool expRet=(*out)[outPortNumber]=='1' || (*out)[outPortNumber]=='4';
		if (!pla->type().compare("f"))
		{
			if (!expRet) expRet=ret;
		}
		else if (!pla->type().compare("fd"))
		{
			if (!expRet) expRet=ret;
		}
		else if (!pla->type().compare("fr"))
		{
			if ((*out)[outPortNumber]=='~' || (*out)[outPortNumber]=='-') expRet=ret;
		}
		else if (!pla->type().compare("fdr"))
		{
			if ((*out)[outPortNumber]=='~' || (*out)[outPortNumber]=='-') expRet=ret;
		}
		if (ret!=expRet) return false;
	}

	return true;
}

hub::SymFunc::Func Decomposer::getHubFunc(GateType type)
{
	//Preklad typu GateType na hub::SymFunc
	switch (type)
	{
	case FALSE:
		return hub::SymFunc::fT0;
	case TRUE:
		return hub::SymFunc::fT1;
	case PORT:
	case INVPORT:
		return hub::SymFunc::fNull;
	case BUF:
		return hub::SymFunc::fBuf;
	case INV:
		return hub::SymFunc::fInv;
	case AND:
		return hub::SymFunc::fAnd;
	case OR:
		return hub::SymFunc::fOr;
	case XOR:
		return hub::SymFunc::fXor;
	case NAND:
		return hub::SymFunc::fNand;
	case NOR:
		return hub::SymFunc::fNor;
	case XNOR:
		return hub::SymFunc::fXnor;
	default:
		return hub::SymFunc::fNull;
	}
}

bool Decomposer::getKernels(Gate* gate,Kernels& _kernels,std::set<int>& usedPorts,std::set<int>::iterator currentPort,Cube& currentCoKernel,KernelsSize& _kernelsSize,int _optimalizationKS)
{
	//Pokud je hradlo odpojeno, kernel dale nehledame
	if (gate->disabled) return false;

	bool kernelAdded=false;
	bool removeKernel=false;
	Kernel kernel;

	//Pokud je rovnice cube-free a co-kernel je nenulový, pridame kernel do seznamu
	if (!currentCoKernel.empty() && gate->isCubeFree(true))
	{
		Cube kernelCube;
		Gates representation;

		if (gate->isProduct(true))
		{
			if (gate->type==PORT)
				kernelCube.insert(gate->portNumber);
			else if (gate->type==INVPORT)
				kernelCube.insert(-gate->portNumber);
			else for (Gates::iterator iG=gate->inputs.begin();iG!=gate->inputs.end();iG++)
				if (!(*iG)->disabled)
					switch ((*iG)->type)
				{
					case PORT:
						kernelCube.insert((*iG)->portNumber);
						break;
					case INVPORT:
						kernelCube.insert(-(*iG)->portNumber);
						break;
				}
				kernel.insert(kernelCube);
				representation.insert(gate);
		}
		else for (Gates::iterator iG=gate->inputs.begin();iG!=gate->inputs.end();iG++)
			if (!(*iG)->disabled)
			{
				if ((*iG)->type==PORT)
					kernelCube.insert((*iG)->portNumber);
				else if ((*iG)->type==INVPORT)
					kernelCube.insert(-(*iG)->portNumber);
				else for (Gates::iterator iPG=(*iG)->inputs.begin();iPG!=(*iG)->inputs.end();iPG++)
					if (!(*iPG)->disabled)
						switch ((*iPG)->type)
					{
						case PORT:
							kernelCube.insert((*iPG)->portNumber);
							break;
						case INVPORT:
							kernelCube.insert(-(*iPG)->portNumber);
							break;
					}
					kernel.insert(kernelCube);
					kernelCube.clear();
					representation.insert(*iG);
			}

			_kernels[kernel][gate].kernel=kernel;
			_kernels[kernel][gate].function=gate;
			_kernels[kernel][gate].coKernels.insert(currentCoKernel);
			_kernels[kernel][gate].representation.insert(representation.begin(),representation.end());

			_kernelsSize.insert(kernel);

			//Optimalizace, odstranime kernely nad pocet _optimalizationKS, dle poctu literalu
			while (_optimalizationKS>-1 && _kernels.size()>_optimalizationKS && !_kernelsSize.empty())
			{
				Kernel remove=(*_kernelsSize.begin());
				_kernelsSize.erase(remove);
				_kernels.erase(remove);
			}

			kernelAdded=true;
	}

	//Rekurzivni volani pro vydeleni dalsich literalu
	for (std::set<int>::iterator i=currentPort;i!=usedPorts.end();i++)
	{
		Cube coKernel=currentCoKernel;
		coKernel.insert(*i);
		gate->disablePort(*i);

		if (!gate->disabled)
		{
			std::map<int,int> portsCount;
			gate->getPortsCount(portsCount,true);

			bool execGetKernels=true;
			int cubesCount=gate->cubesCount(true);

			for (std::set<int>::iterator j=usedPorts.begin();j!=i;j++)
				if (portsCount[*j]>=cubesCount)
				{
					execGetKernels=false;
					break;
				}

				if (execGetKernels)
				{
					std::set<int>::iterator nextPort=i;
					for (nextPort++;nextPort!=usedPorts.end() && !portsCount[*nextPort];nextPort++);
					if (getKernels(gate,_kernels,usedPorts,nextPort,coKernel,_kernelsSize,_optimalizationKS))
						removeKernel=true;
				}
		}

		gate->enablePort(*i);
	}

	//if (kernelAdded && removeKernel)
	//	_kernels.erase(kernel);

	return kernelAdded;
}

void Decomposer::getKernels(Gate* gate,Kernels& _kernels,KernelsSize& _kernelsSize,int _optimalizationKS)
{
	std::set<int> usedPorts;
	Cube coKernel;

	gate->getUsedPorts(usedPorts);

	getKernels(gate,_kernels,usedPorts,usedPorts.begin(),coKernel,_kernelsSize,_optimalizationKS);
}

bool Decomposer::chooseKernel(Kernels& _kernels,KernelValues& _kernelValues,bool _multiGain)
{
	//Vyber kernelu s nejvetsim ziskem
	if (_kernels.empty()) return false;

	bool found=false;
	int gain,maxGain=0;
	for (Kernels::iterator iK=_kernels.begin();iK!=_kernels.end();iK++)
	{
		if ((gain=computeKernelGain(_kernels,iK->first,_multiGain))>maxGain || !found)
		{
			maxGain=gain;
			_kernelValues=iK->second;
			found=true;
		}
	}

	return found;
}

int Decomposer::computeKernelGain(Kernels& _kernels,const Kernel& _kernel,bool _multiGain)
{
	//Vypocet zisku kernelu
	if (_kernels[_kernel].empty())
		return 0xf0000000;

	if (_kernels[_kernel].begin()->second.gainCache!=0xf0000000)
		return _kernels[_kernel].begin()->second.gainCache;

	int kL=0;
	int ckL=0;
	int kC=_kernel.size();
	int ckC=0;
	int kG=1;

	Gates usedGates;

	for (Kernel::const_iterator cube=_kernel.begin();cube!=_kernel.end();cube++)
		kL+=cube->size();

	for (KernelValues::iterator kernelValue=_kernels[_kernel].begin();kernelValue!=_kernels[_kernel].end();kernelValue++)
	{
		ckC+=kernelValue->second.coKernels.size();
		for (CoKernels::iterator cube=kernelValue->second.coKernels.begin();cube!=kernelValue->second.coKernels.end();cube++)
			ckL+=cube->size();
		if (_multiGain)	usedGates.insert(kernelValue->second.representation.begin(),kernelValue->second.representation.end());
	}

	if (_multiGain)	for (Kernels::iterator iK=_kernels.begin();iK!=_kernels.end();iK++)
	{
		KernelValues::iterator kernelValue;
		for (kernelValue=iK->second.begin();kernelValue!=iK->second.end();kernelValue++)
		{
			Gates::iterator iG;
			for (iG=kernelValue->second.representation.begin();iG!=kernelValue->second.representation.end();iG++)
				if (usedGates.find(*iG)!=usedGates.end())
					break;
			if (iG!=kernelValue->second.representation.end())
				break;
		}
		if (kernelValue==iK->second.end())
			kG++;
	}

	int gain=(kC*ckL+ckC*kL-ckL-kL)*kG;

	for (KernelValues::iterator kernelValue=_kernels[_kernel].begin();kernelValue!=_kernels[_kernel].end();kernelValue++)
		kernelValue->second.gainCache=gain;

	return gain;
}

void Decomposer::removeKernel(Kernels& _kernels,Kernel& _kernel)
{
	//Odstranime zavisle kernely
	Gates usedGates;
	std::set<Kernel> removeKernels;

	for (KernelValues::iterator kernelValue=_kernels[_kernel].begin();kernelValue!=_kernels[_kernel].end();kernelValue++)
		usedGates.insert(kernelValue->second.representation.begin(),kernelValue->second.representation.end());

	for (Kernels::iterator iK=_kernels.begin();iK!=_kernels.end();iK++)
	{
		KernelValues::iterator kernelValue;
		for (kernelValue=iK->second.begin();kernelValue!=iK->second.end();kernelValue++)
		{
			Gates::iterator iG;
			for (iG=kernelValue->second.representation.begin();iG!=kernelValue->second.representation.end();iG++)
				if (usedGates.find(*iG)!=usedGates.end())
					break;
			if (iG!=kernelValue->second.representation.end())
				break;
		}
		if (kernelValue!=iK->second.end())
			removeKernels.insert(iK->first);
	}

	for (std::set<Kernel>::iterator iR=removeKernels.begin();iR!=removeKernels.end();iR++)
		_kernels.erase(*iR);
}

Decomposer::Decomposer(hub::Hub* _theHub)
{
	theHub=_theHub;
}

Decomposer::~Decomposer()
{
	if (statsFile)
		fclose(statsFile);
}

int Decomposer::operator()(hub::Location& _locationNames,std::string& _libGatesName,bool _convertTo2Input,bool _convertToNAND,std::string statsFileName,bool _printDebugInfo,int _optimalizationKS,bool _multiGain)
{
	if (!theHub) return -1;

	libGates=theHub->FindOrCreateLib(_libGatesName);
	library=_locationNames.theLib();//theHub->FindLib(_locationNames.libName());
	entity=_locationNames.theEntity();//library->FindEntity(_locationNames.entityName());
	architecture=_locationNames.theArch();//entity->FindArch(_locationNames.archName());

	statsFile=NULL;

	if (!statsFileName.empty())
		statsFile=fopen(statsFileName.c_str(),"wt");

	_printDebugInfo=_printDebugInfo && statsFile;

	int ret=-1;

	if (!libGates) return -1;
	if (!library) return -1;
	if (!entity) return -1;
	if (!architecture) return -1;

	hub::BehavArch* behavArch=dynamic_cast<hub::BehavArch*>(architecture->Impl());
	if (!behavArch) return -1;
	hub::PlaDescr* pla=behavArch->description<hub::PlaDescr>();
	if (!pla) return -1;

	if (!pla->outputNo()) return 0;

	char buff[4];
	int i=0;

	strArchitecture=entity->FindOrCreateArch("gatenet");
	strArch=new hub::StructArch();
	strArchitecture->Impl(strArch);
	for (hub::Entity::PortProtoIter iP=entity->PortProtoBeg();iP!=entity->PortProtoEnd();iP++)
	{
		hub::Port<hub::StructArch>* port=strArch->CreatePort(iP->second);
		if (hub::PortProto::inDir.IsSubset(port->flags()))
		{
			hub::Net* net=strArch->FindOrCreateNet(std::string("i")+itoa(i++,buff,10));
			net->Join(port);
		}
	}

	if (statsFile)
	{
		fprintf(statsFile,"Legend:\n");
		fprintf(statsFile,"INV  ... !\n");
		fprintf(statsFile,"AND  ... *\n");
		fprintf(statsFile,"OR   ... +\n");
		fprintf(statsFile,"XOR  ... ^\n");
		fprintf(statsFile,"NAND ... &\n");
		fprintf(statsFile,"NOR  ... |\n");
		fprintf(statsFile,"XNOR ... @\n\n");

		fprintf(statsFile,"%sINPUTS:\n%s\n",gDashes,gDashes);
	}

	//Postavime strukturu site hradel z PLA
	Gate** outputs=new Gate*[pla->outputNo()];
	for (int i=0;i<pla->outputNo();i++)
	{
		outputs[i]=buildGateNetFromPLA(pla,i);
		if (statsFile)
		{
			fprintf(statsFile,"o%d = %s\n\n",i,outputs[i]->toString().c_str());
			fprintf(statsFile,"literals count [o%d]:\t%d\n",i,outputs[i]->literalsCount());
			fprintf(statsFile,"gates count [o%d]:\t%d\n",i,outputs[i]->gatesCount(true));
			fprintf(statsFile,"levels [o%d]:\t\t%d\n\n\n",i,outputs[i]->getLevels());
		}
#ifdef DEBUG
		if (!verify(outputs[i],pla,i))
			printf("!not equal: %d\n",i);
#endif
	}
#ifdef WIN32
	DWORD gtcFrom=GetTickCount();
#endif

	//Provedeme dekompozici
	decomposeGates(outputs,pla->outputNo(),_optimalizationKS,_multiGain,_printDebugInfo?statsFile:NULL);

#ifdef DEBUG
	for (int i=0;i<pla->outputNo();i++)
		if (!verify(outputs[i],pla,i))
			printf("not equal: %d\n",i);
#endif

#ifdef WIN32
	DWORD gtcTo=GetTickCount();
#endif

	if (statsFile)
		fprintf(statsFile,"%sPRE-OUTPUTS:\n%s\n",gDashes,gDashes);

	//Uprava po dekompozici
	for (int i=0;i<pla->outputNo();i++)
	{
		Gate* output=outputs[i];
		output->reduceGates();
		if (statsFile)
		{
			fprintf(statsFile,"o%d = %s\n\n",i,outputs[i]->toString().c_str());
			fprintf(statsFile,"literals count [o%d]:\t%d\n",i,outputs[i]->literalsCount());
			fprintf(statsFile,"gates count [o%d]:\t%d\n",i,outputs[i]->gatesCount(true));
			fprintf(statsFile,"levels [o%d]:\t\t%d\n\n\n",i,outputs[i]->getLevels());
		}

		if (_convertTo2Input) output->convertTo2Input();
		if (_convertToNAND) output->convertToNAND();
		if (_convertTo2Input || _convertToNAND) output->reduceGates();

		hub::Port<hub::StructArch>* portY=strArch->FindPort(i,hub::PortProto::outDir);
		hub::Net* net;
		if (ret=connect(output,&net)) return ret;
		net->Join(portY);

		output->setDisabled(false);
		output->setRelease(false);
	}

	//Uvlonime strukturu z pameti
	Gate::releaseGates();

	//Zapisemse statisticke udaje
	if (statsFile && (_convertTo2Input || _convertToNAND))
	{
		fprintf(statsFile,"%sFINAL OUTPUTS:\n%s\n",gDashes,gDashes);
		for (int i=0;i<pla->outputNo();i++)
		{
			fprintf(statsFile,"o%d = %s\n\n",i,outputs[i]->toString().c_str());
			fprintf(statsFile,"literals count [o%d]:\t%d\n",i,outputs[i]->literalsCount());
			fprintf(statsFile,"gates count [o%d]:\t%d\n",i,outputs[i]->gatesCount(true));
			fprintf(statsFile,"levels [o%d]:\t\t%d\n\n\n",i,outputs[i]->getLevels());
		}

	}

	bool verified=true;

	for (int i=0;i<pla->outputNo();i++)
		if (!verify(outputs[i],pla,i))
		{
			verified=false;
			break;
		}

		if (statsFile)
		{
			int totalGates=0;
			int totalInvPorts=0;
			std::set<int> usedPorts;
			int level,maxLevel=0;
			unsigned long decompositionTime=0;
#ifdef WIN32
			decompositionTime=gtcTo-gtcFrom;
#endif

			for (int i=0;i<pla->outputNo();i++)
			{
				if ((level=outputs[i]->getLevels())>maxLevel)
					maxLevel=level;
				totalGates+=outputs[i]->gatesCount(false,true);
				outputs[i]->getUsedPorts(usedPorts);
				outputs[i]->setRelease(true);
			}

			for (std::set<int>::iterator port=usedPorts.begin();port!=usedPorts.end();port++)
				if (*port<0)
					totalInvPorts++;

			fprintf(statsFile,"%sGENERAL INFO:\n%s\n",gDashes,gDashes);
			fprintf(statsFile,"total gates count:\t%d = %d (gates) + %d (inv ports)\n",totalGates+totalInvPorts,totalGates,totalInvPorts);
			fprintf(statsFile,"levels:\t\t\t%d\n",maxLevel);
#ifdef WIN32
			fprintf(statsFile,"decomposition time:\t%d ms\n",decompositionTime);
#endif
			fprintf(statsFile,"verified:\t\t%d\n\n",verified);
		}
		else for (int i=0;i<pla->outputNo();i++)
			outputs[i]->setRelease(true);

		//Uvolnime z pameti
		Gate::releaseGates();

		delete[] outputs;

		return 0;
}

void Decomposer::decomposeGates(Gate** outputs,int outputsCount,int _optimalizationKS,bool _multiGain,FILE* statsFile)
{
	int functionsCount=0;
	int toDecomposeCount=outputsCount;
	Gate** functions=(Gate**)malloc(sizeof(Gate*)*functionsCount);
	Gate** toDecompose=(Gate**)malloc(sizeof(Gate*)*toDecomposeCount);
	memcpy(toDecompose,outputs,sizeof(Gate*)*toDecomposeCount);

	int functionsOffset=outputsCount;

	for (int i=0;i<outputsCount;i++)
	{
		std::set<int> usedPorts;

		outputs[i]->getUsedPorts(usedPorts);
		for (std::set<int>::iterator port=usedPorts.begin();port!=usedPorts.end();port++)
			if (abs(*port)>=functionsOffset)
				functionsOffset=abs(*port)+1;
	}

	int nextFunctionPort=functionsOffset;

	if (statsFile)
		fprintf(statsFile,"%s----->***DEBUG INFO***\n%s\n",gDashes,gDashes);

	bool exitDecomposition=false;
	while (toDecomposeCount && !exitDecomposition)
	{
#ifdef WIN32
		DWORD gtcFrom=GetTickCount();
#endif
		Kernels kernels;
		KernelsSize kernelsSize;

		//Vytvorime seznam kernelu
		for (int i=0;i<toDecomposeCount;i++) 
			getKernels(toDecompose[i],kernels,kernelsSize,_optimalizationKS);

		int kernelsCount=kernels.size();
		int kernelsUsed=0;
		exitDecomposition=true;

		KernelValues kernelValues;
		while (chooseKernel(kernels,kernelValues,_multiGain))
		{
			//Po vybrani kernelu nahradime vyber za nove funkce, reprezentovane novou promennou
			kernelsUsed++;
			for (KernelValues::iterator kernelValue=kernelValues.begin();kernelValue!=kernelValues.end();kernelValue++)
			{
				Gate* function=new Gate(OR);
				for (Kernel::iterator cube=kernelValue->second.kernel.begin();cube!=kernelValue->second.kernel.end();cube++)
				{
					Gate* kernelCube=new Gate(AND);
					if (cube->empty())
						kernelCube->type=TRUE;
					else for (Cube::iterator lit=cube->begin();lit!=cube->end();lit++)
					{
						Gate* portInput=new Gate((*lit)>0?PORT:INVPORT,abs(*lit));
						kernelCube->addInput(portInput);
					}
					function->addInput(kernelCube);
				}

				functions=(Gate**)realloc(functions,sizeof(Gate*)*(++functionsCount));
				functions[functionsCount-1]=function;
				toDecompose=(Gate**)realloc(toDecompose,sizeof(Gate*)*(++toDecomposeCount));
				toDecompose[toDecomposeCount-1]=function;

				Gate* var=new Gate(PORT,nextFunctionPort++);
				for (Gates::iterator iG=kernelValue->second.representation.begin();iG!=kernelValue->second.representation.end();iG++)
					kernelValue->first->removeInput(*iG);
				for (CoKernels::iterator cube=kernelValue->second.coKernels.begin();cube!=kernelValue->second.coKernels.end();cube++)
				{
					Gate* coKernelCube=new Gate(AND);
					for (Cube::iterator lit=cube->begin();lit!=cube->end();lit++)
					{
						Gate* portInput=new Gate((*lit)>0?PORT:INVPORT,abs(*lit));
						coKernelCube->addInput(portInput);
					}
					coKernelCube->addInput(var);
					kernelValue->first->addInput(coKernelCube);
				}
			}
			exitDecomposition=false;
			//Aktualizujeme seznam kernelu
			removeKernel(kernels,kernelValues.begin()->second.kernel);
		}
#ifdef WIN32
		DWORD gtcTo=GetTickCount();
#endif

		if (!exitDecomposition && statsFile)
		{
			fprintf(statsFile,"***NEXT ITERATION\n");
			fprintf(statsFile,"kernels found: %d\nkernels used: %d\n",kernelsCount,kernelsUsed);
#ifdef WIN32
			fprintf(statsFile,"iteration time: %d ms\n",gtcTo-gtcFrom);
#endif
			fprintf(statsFile,"\n");
			for (int idc=0;idc<outputsCount;idc++)
				fprintf(statsFile,"o%d = %s\n\n",idc,toDecompose[idc]->toString().c_str());
			for (int idc=outputsCount;idc<toDecomposeCount;idc++)
				fprintf(statsFile,"%c = %s\n\n",'a'+functionsOffset+idc-outputsCount-1,toDecompose[idc]->toString().c_str());

			fflush(statsFile);
		}
	}

	//Po dekompozici nahradime promenne zpet za funkce
	for (int i=0;i<toDecomposeCount;i++)
		toDecompose[i]->replaceFunctions(functionsOffset,functions,functionsCount);

	if (statsFile)
	{
		fprintf(statsFile,"***NOT REDUCED\n");
		for (int i=0;i<outputsCount;i++)
		{
			fprintf(statsFile,"o%d = %s\n\n",i,toDecompose[i]->toString().c_str());
			fprintf(statsFile,"literals count [o%d]:\t%d\n",i,toDecompose[i]->literalsCount());
			fprintf(statsFile,"gates count [o%d]:\t%d\n",i,toDecompose[i]->gatesCount(true));
			fprintf(statsFile,"levels [o%d]:\t\t%d\n\n\n",i,toDecompose[i]->getLevels());
		}

		fprintf(statsFile,"%s<-----***DEBUG INFO***\n%s\n",gDashes,gDashes);
	}

	free(functions);
	free(toDecompose);
}