// ============= Complement by Jan Kucera (2010) =====================

#include "stdafx.h"

using namespace std;


#include "kernelincludes.h"
#include "Complement.h"


// constructor
Complement::Complement()
{
	containment = 0;
	merge_shared = true;
}

// destructor
/*Complement::~Complement()
{
}*/

// Returns the index of input variable
// which is the best for splitting variable.
// If the given cover if unate, no index is
// chosen and (-1) is returned. 
int Complement::BinateSelect( const SoPLA * pla ) const
{
	int res = 0;
	unsigned int n = pla->inputs();
    unsigned int * p0 = new unsigned int[n];
    unsigned int * p1 = new unsigned int[n];
    unsigned int * min01 = new unsigned int[n];
    unsigned int my_max;
	unsigned int i;
	CubeList::const_iterator ti; 

    for (i = 0; i < n; i++)
		p0[i] = p1[i] = min01[i] = 0;
    
	for ( ti = pla->getConstDataRef().begin(); ti!=pla->getConstDataRef().end(); ++ti )	{
		for ( i = 0; i < n; i++ ) {
			if (ti->isLit_0(i)) p0[i]++;
			if (ti->isLit_1(i)) p1[i]++;
		}
	}
	my_max = 0;
	for (i = 0; i < n; i++) {
		min01[i] = min(p0[i], p1[i]);
		if ( my_max < min01[i] ) my_max = min01[i];
	}

	if (my_max == 0)	// it is a unate function and no splitting variable is chosen
	{
		res = (-1);
	} else {		// looking for splitting variable
		res = 0;
		my_max = p0[res]+p1[res];
		for (i = 1; i < n; i++)
			if ((min01[i] > 0)&&((p0[i]+p1[i]) > my_max)) {
				res = i;
				my_max = p0[i]+p1[i];
			}
	}
	delete p0;
	delete p1;
	delete min01;
	return res;	
}

// returns a SoPLA as a result of merging given SoPLAs
// ind is index of splitting variable
// uses macro CONTAINMENT which decides the behavior of
// this function: 
//	     CONTAINMENT=0   ...  MergeWithIdentity
//		 CONTAINMENT=1   ...  MergeWithContainment
SoPLA * Complement::MergeWithContainment(SoPLA* pla0, SoPLA* pla1, const unsigned int ind) const
{
	if ((pla0 == NULL)||(pla1 == NULL))
		throw RuntimeException("MergeWithContainment: Arguments cannot be NULL.");
	if (pla0->inputs() != pla1->inputs())
		throw RuntimeException("MergeWithContainment: Incompatible PLAs.");
	SoPLA * pla2 = new SoPLA(pla0);
	if (pla2 == NULL)
		throw RuntimeException("MergeWithContainment: Not enough memory.");
	CubeList::iterator ti0, ti1;
	bool flag;
	
	if ( !containment ) {
		ti0 = pla0->getData()->begin();
		while ( ti0 != pla0->getData()->end() ) {
			ti1 = pla1->getData()->begin();
			flag = false;
			while ( ti1 != pla1->getData()->end() )
				if ( *ti0 == *ti1 ) {
					pla2->AppendTerm(*ti0);
					ti0 = pla0->DeleteTerm(ti0);
					pla1->DeleteTerm(ti1);
					flag = true;
					break;
				} else ti1++;
			if ( !flag ) ti0++;
		}
	} else {
		ti0 = pla0->getData()->begin();					// PF: this originally couldn't have worked
		while ( ti0 != pla0->getData()->end() ) {
			ti1 = pla1->getData()->begin();
			while ( ti1 != pla1->getData()->end() )
				if ( ti0->contains(*ti1) ) {
					pla2->AppendTerm(*ti1);
					ti1 = pla1->DeleteTerm(ti1);
				} else if ( ti1->contains(*ti0) ) {
					pla2->AppendTerm(*ti0);
					ti0 = pla1->DeleteTerm(ti0);
					flag = false;
					break;
				} else ti1++;
			if ( !flag ) ti0++;
		}
	}
	
	for( ti0 = pla0->getData()->begin(); ti0 != pla0->getData()->end(); ++ti0 )
		ti0->setLit_0(ind);
	pla2->Splice(*pla0);

	for( ti1 = pla1->getData()->begin(); ti1 != pla1->getData()->end(); ++ti1 )
		ti1->setLit_1(ind);
	pla2->Splice(*pla1);

	return pla2;
}

// prints the personality 
// matrix to the output
// (only for testing)
void Complement::PrintPersonalityMatrix(SoPLA * pers_m)
{
	if (pers_m == NULL)
		throw RuntimeException("PrintPersonalityMatrix: Argument cannot be NULL.");
	for (CubeList::const_iterator ti=pers_m->getData()->begin(); ti!=pers_m->getData()->end(); ++ti)
		for (unsigned int i=0; i < ti->inputs(); i++)
			if ( ti->isLit_dc(i) ) throw RuntimeException("PrintPersonalityMatrix: Not valid personal matrix."); 
	cout << "Personality Matrix: " << endl;
	for (CubeList::const_iterator ti=pers_m->getData()->begin(); ti!=pers_m->getData()->end(); ++ti)
		cout << ti->getCube() << endl;
	cout << endl;
}


// prints given v-matrix to the output
// (only for testing)
void Complement::PrintTranslationMatrix(const Cube * V)
{
	if (V == NULL)
		throw RuntimeException("PrintTranslationMatrix: Argument cannot be NULL.");
	cout << "V-Matrix: " << endl;
	cout << V->getCube() << endl;
}


// returns the spliting variable for 
// pers_unate_complement function
unsigned int Complement::UnateComplement_SplitSelect(const SoPLA * pers_mat) const
{
	if ( pers_mat == NULL )
		throw RuntimeException("UnateComplement_SplitSelect: Argument cannot be NULL.");

	unsigned int i, k;
	unsigned int res = 0;
	CubeList::const_iterator ti;
   	unsigned int n = pers_mat->getTerms();
	unsigned int * zero_cnt = new unsigned int[n];
	if ( zero_cnt == NULL ) throw RuntimeException("UnateComplement_SplitSelect: Not enough memory.");
	for ( i = 0; i < n; i++ ) zero_cnt[i] = 0;
		
	// find the largest cube
	CubeList::const_iterator largest;
	unsigned int my_max = 0;
	unsigned int cnt;

	for ( ti = pers_mat->getConstDataRef().begin(); ti != pers_mat->getConstDataRef().end(); ++ti ) {
		cnt = 0;
		for ( k = 0; k < pers_mat->inputs(); k++ )
			if ( ti->isLit_0(k) ) cnt++;
		if ( my_max < cnt ) largest = ti;
	} 
	
	// select set of variables that are used in
	// the largest cube (found above)
	int * used_vars = new int[pers_mat->inputs()];
	if (used_vars == NULL) throw RuntimeException("UnateComplement_SplitSelect: Not enough memory.");

	for ( i = 0; i < pers_mat->inputs(); i++ )	{
		if ( largest->isLit_1(i) ) used_vars[i] = 0;
		else used_vars[i] = (-1);
	}
	
	// count how much are these 
	// variables (selected right above) used
	// and select the varible, which
	// apears most frequently in other terms
	for ( i = 0; i < pers_mat->inputs(); i++ ) {
		for ( ti = pers_mat->getConstDataRef().begin(); ti != pers_mat->getConstDataRef().end(); ++ti) {
			if ( (ti->isLit_1(i)) && ( used_vars[i] >= 0 ) )	used_vars[i]++;
		}
		if (used_vars[res] < used_vars[i])
			res = i;
	}

	delete zero_cnt;
	delete used_vars;
	return res;
}


// returns personal matrix cofactors
// if sign==0	M0 is returned
// if sign==1	M1 is returned
SoPLA * Complement::PersCofactors( const SoPLA * pers_mat, const unsigned int j, const bool sign ) const
{
	CubeList::const_iterator ti;

	if (pers_mat == NULL)
		throw RuntimeException("PersCofactiors: Argument cannot be NULL.");
	if ( j >= pers_mat->inputs() )
		throw RuntimeException("PersCofactiors: Invalid argument.");

	SoPLA * res = new SoPLA(pers_mat);
	if (res == NULL)
		throw RuntimeException("PersCofactiors: Not enough memory.");

	for ( ti = pers_mat->getConstDataRef().begin(); ti != pers_mat->getConstDataRef().end(); ++ti ) {
		if (!sign) {
			if ( ti->isLit_0(j) ) res->AppendTerm(*ti);
		} else {
			Cube x(*ti);
			if (!x.isLit_0(j)) x.setLit_0(j);
			res->AppendTerm(x);
		}
	}

	return res;	
}

// Demorgan's Law - only for use in UnateComplemtation 
// !!! the input is an iterator to PersonalityMatrix term !!!
// !!! never pass a standart SoPLA term to this function !!!
SoPLA * Complement::DemorganForUC( const CubeList::const_iterator ti, const SoPLA *pla) const
{
	SoPLA * res = new SoPLA(pla);
	if ( res == NULL ) throw RuntimeException("Demorgan: Not enough memory.");

	for ( unsigned int i = 0; i < ti->inputs(); i++ ) {
		if ( ti->isLit_1(i) ) {
			Cube c(ti->inputs());
			c.assign(ZERO);
			c.setLit_1(i);
			res->AppendTerm(c);
		}
	}
	return res;
}

// computes Complement of Personal Unate Matrix
SoPLA * Complement::Pers_Unate_Complement( const SoPLA * pers_mat) const
{
	CubeList::const_iterator ti;
	CubeList::iterator ti0, ti1;
	unsigned int i, j;
	bool pom;

	if (pers_mat == NULL) throw RuntimeException("Pers_Unate_Complement: Argument cannot be NULL.");
	SoPLA * res = new SoPLA(pers_mat);
	if (res == NULL) throw RuntimeException("Pers_Unate_Complement: Not enough memory.");

	// 1. special case - given M is empty
	// the complement is tautology
	if ( pers_mat->empty() ) {
		Cube c(pers_mat->inputs());
		c.assign(ZERO);
		res->AppendTerm(c);
		return res;
	}

	// 2. secial case - there is a row of all0s
	// the complement is empty
	for ( ti = pers_mat->getConstDataRef().begin(); ti != pers_mat->getConstDataRef().end(); ++ti ) {
		for ( i = 0; i < pers_mat->inputs(); i++ )
			if ( !ti->isLit_0(i) ) break;
		if ( i == pers_mat->inputs() ) return res;
	}

	// 3. special case - M has only one term
	// we use Demorgan's law to compute the complement
	if ( pers_mat->getTerms() == 1 ) {
		SoPLA * dem = DemorganForUC(pers_mat->getConstDataRef().begin(), pers_mat);
		res->Splice(*dem);
		delete dem;
		return res;
	}

	// if no special case ocured, choose 
	// the splitting variable and use
	// recursive Pers_Unate_Complement
	j = UnateComplement_SplitSelect(pers_mat);
	SoPLA * M0 = PersCofactors(pers_mat, j, false);
	SoPLA * M1 = PersCofactors(pers_mat, j, true);

	SoPLA * M0_non = Pers_Unate_Complement(M0);
	SoPLA * M1_non = Pers_Unate_Complement(M1);
	

	/************************************************************/
	ti0 = M0_non->getData()->begin();
	while ( ti0 != M0_non->getData()->end() ) {
		ti1 = M1_non->getData()->begin();
		pom = false;
		while ( ti1 != M1_non->getData()->end() ) {
			if ( *ti0 == *ti1 ) {
				res->AppendTerm(*ti1);
				ti1 = M1_non->DeleteTerm(ti1);
				ti0 = M0_non->DeleteTerm(ti0);
				//cout << "DUPLICITA !!!" << endl;
				pom = true;
				break;
			} else ti1++;
		}
		if (!pom) ti0++;
	}
	/************************************************************/


	// merging process
	for ( ti0 = M0_non->getData()->begin(); ti0 != M0_non->getData()->end(); ++ti0 )
		ti0->setLit_1(j);
	res->Splice(*M0_non);
	res->Splice(*M1_non);
	
	delete M0;
	delete M1;
	delete M0_non;
	delete M1_non;

	return res;
}

// computes the peronality matrix
// of given unate function
SoPLA * Complement::GeneratePersonalMatrix( const SoPLA * pla ) const
{
	CubeList::const_iterator ti;
	unsigned int i;

	if ( pla == NULL )
		throw RuntimeException("GeneratePersonalMatrix: Argument cannot be NULL.");
	SoPLA * pers_m = new SoPLA(pla);
    if (pers_m == NULL) throw RuntimeException("GeneratePersonalMatrix: Not enough memory.");
	for ( ti=pla->getConstDataRef().begin(); ti != pla->getConstDataRef().end(); ++ti ) {
		Cube c(pla->inputs());
		for ( i = 0; i < ti->inputs(); i++ ) {
			if ( !ti->isLit_dc(i) ) c.setLit_1(i);
			else c.setLit_0(i);
		}
		pers_m->AppendTerm(c);
	}
	return pers_m;
}


// computes the translation matrix, which
// says whether the given function is monotone
// increasing or decreasing in each variable

// PF: somewhat strange result
Cube * Complement::Monotone(const SoPLA * pla) const
{
	CubeList::const_iterator ti;
	unsigned int i;

	if (pla == NULL)
		throw RuntimeException("Monotone: Argument cannot be NULL.");
	Cube * V = new Cube(pla->inputs());
	if (V == NULL) throw RuntimeException("Monotone: Not enough memory.");
	V->assign(DC);

	for ( ti = pla->getConstDataRef().begin(); ti != pla->getConstDataRef().end(); ++ti )
		for ( i = 0; i < pla->inputs(); i++ )
			if ( !ti->isLit_dc(i) ) V->setLit( i, ti->getLit(i) );

	return V;
}

// translates given personality matrix
// back to function cover
SoPLA* Complement::Translate( const SoPLA * pers_mat, const Cube * V) const
{
	CubeList::const_iterator ti;
	unsigned int i;
	
	if ((pers_mat == NULL)||(V == NULL))
		throw RuntimeException("Translate: Arguments cannot be NULL.");
	SoPLA * res = new SoPLA(pers_mat);
	if (res == NULL) throw RuntimeException("Translate: Not enough memory.");

	for ( ti = pers_mat->getConstDataRef().begin(); ti != pers_mat->getConstDataRef().end(); ++ti )	{
		Cube c(pers_mat->inputs());
		for ( i = 0; i < ti->inputs(); i++ ) {
			if ( ti->isLit_0(i) ) c.setLit_dc(i);
			else {
			   if ( V->isLit_0(i) ) c.setLit_1(i);
			   else if ( V->isLit_1(i) ) c.setLit_0(i);
			} 
		}
		res->AppendTerm(c);
	}

	return res;
}


// computes complement of a unate function
SoPLA * Complement::UnateComplement(const SoPLA * pla) const
{
	if (pla == NULL)
		throw RuntimeException("UnateComplement: Argument cannot be NULL.");

	SoPLA * res = NULL;

	// compute personality matrix
	SoPLA * pers_m = GeneratePersonalMatrix(pla);

	// compute translation matrix V
	Cube * V = Monotone(pla);

	SoPLA * M_non = Pers_Unate_Complement(pers_m);
	res = Translate(M_non, V);

	delete V;
	delete pers_m;
	delete M_non;

	return res;
}


// This function says, whether given
// SoPLA is unate or not.
// This funcion is not used in the
// complement-algorithm. CheckSpecialCases
// is used instead.
bool Complement::IsUnate(const SoPLA * pla) const
{
	CubeList::const_iterator ti;
	unsigned int i;
	Literal z;

	if ( pla == NULL )
		throw RuntimeException("IsUnate: Argument cannot be NULL.");
	if ( pla->empty() ) 
		return false;					//throw RuntimeException("IsUnate: Zero terms in pla.");
	if ( pla->getTerms() == 1 )			// je li tam jen jedna krychle tak to je UNATE FUNCTION
		return true;

	Cube c(pla->inputs());
	c.assign(DC);

	for ( ti = pla->getConstDataRef().begin(); ti != pla->getConstDataRef().end(); ++ti ) {
		for ( i = 0; i < pla->inputs(); i++ ) {
			z = ti->getLit2(i);
			if ( z == ONE ) {
				if ( c.isLit_dc(i) ) c.setLit_1(i);
				else if ( c.isLit_0(i) ) return false;
			}
			if ( z == ZERO ) {
				if ( c.isLit_dc(i) ) c.setLit_0(i);
				else if ( c.isLit_1(i) ) return false;
			}
		}
	} 
	return true;
}


// This function is used to check 
// all the three special cases that
// can ocure at the beginning of
// SingleOutputComplement function.
// If the given SoPLA is unate, the return value is NULL
// If the given SoPLA contains a row of all 2s ('-') -> the
//		retuen value is an instance of MultiResult Class 
//		containin variable 'all2s' set to TRUE
// If the given function contains at least one column of 
//      all 0s or all 1s -> the instance of MultiResult Class
//		indicates which columns these are.
// The instance of MultiResult Class also says, which variable
// should be chosen as SPLITTING VARIABLE
// (more about MultiResult Class can be found in Complement.h)
Complement::MultiResult* Complement::CheckSpecialCases(const SoPLA * pla) const
{
	if ( pla == NULL )
		throw RuntimeException("IsUnate: Argument cannot be NULL.");
	if ( pla->empty() ) 
		return NULL;					//throw RuntimeException("IsUnate: Zero terms in pla.");
	if ( pla->getTerms() == 1 )			// je li tam jen jedna krychle tak to je UNATE FUNCTION
		return NULL;
	
	
	int res = 0;
	unsigned int n = pla->inputs();
    unsigned int * p0 = new unsigned int[n];
    unsigned int * p1 = new unsigned int[n];
    unsigned int * min01 = new unsigned int[n];
	unsigned int my_max;
	bool all2s;
	unsigned int i, j;
	CubeList::const_iterator ti;

	Cube * mapa = new Cube(n);
	mapa->assign(DC);

    for ( i = 0; i < n; i++ )
		p0[i] = p1[i] = min01[i] = 0;
  
	for ( ti = pla->getConstDataRef().begin(); ti!=pla->getConstDataRef().end(); ++ti )	{
		all2s = true;
		for ( j = 0; j < n; j++ ) {
			if ( ti->isLit_0(j) ) {
				p0[j]++;
				all2s = false;
			} else if ( ti->isLit_1(j) ) {
				p1[j]++;
				all2s = false;
			}
		}
		if (all2s) {
			delete p0;
			delete p1;
			delete min01;
			return new MultiResult(mapa, 0, true);
		}
	}
	my_max = 0;
	for ( j = 0; j < n; j++ ) {
		min01[j] = min(p0[j], p1[j]);
		if ( my_max < min01[j] )
		   my_max = min01[j];
		if ( max(p0[j], p1[j] ) == pla->getTerms() ) {
			if (p0[j] == pla->getTerms()) mapa->setLit_0(j);
			if (p1[j] == pla->getTerms()) mapa->setLit_1(j);
		}
	}

	if (my_max == 0)	// it is a unate function and no splitting variable is chosen
	{
		res = (-1);
	} else {		// looking for splitting variable
		
		// heuristika H3: kombinovana heuristika (scitaci + nasobici)
		/*
		res = 0;
		while ((p0[res] == 0) && (p1[res] == 0)) res++;
		my_max = p0[res] + p1[res];
		for ( j = res+1; j < n; j++ )
		  if (min01[j] > 0) {
			if ( p0[j] + p1[j] > my_max) {
				res = j;
				my_max = p0[j] + p1[j];
			} else {
				if ( p0[j] + p1[j]  ==  my_max)
					if (p0[j] * p1[j] > p0[res] * p1[res])
						res = j;
			}
		  }	
		*/

		// to je mereni2 - heuristika scitaci
		res = 0;
		while ( (p0[res] == 0) || (p1[res] == 0) ) res++;
		my_max = p0[res] + p1[res];
		for ( j = res+1; j<n; j++ )
		  if ( min01[j] > 0 ) {
			if ( p0[j] + p1[j] > my_max) {
				res = j;
				my_max = p0[j] + p1[j];
			} 
		  }	


		// Heuristika H1 - nasobici
		/*
		res = 0;
		while ( p0[res] * p1[res] == 0 ) res++;
		my_max = p0[res] * p1[res];
		for ( j = res+1; j < n; j++ )
			if (p0[j] * p1[j] > my_max) {
				res = j;
				my_max = p0[j] * p1[j];
			}
		*/

	}

	delete p0;
	delete p1;
	delete min01;

	if (res == -1)
	{
		delete mapa;
		return NULL;
	} else {
		MultiResult * mr = new MultiResult(mapa, res, false);
		return mr;
	}
}

// this function computes the complement of given
// single output function (OnSet-DcSet)
// Modifies the source function!
SoPLA * Complement::SingleOutputComplement(SoPLA * fd) const
{
	if (fd == NULL)
		throw RuntimeException("SingleOutputComplement: Argument cannot be NULL.");
	
	SoPLA * result = new SoPLA(fd);
	if (result == NULL) 
		throw RuntimeException("SingleOutputComplement: Not enough memory.");
	
	MultiResult * u = CheckSpecialCases(fd);

	// First Special Case
	// If this function contains a cube of all 2s,
	// the complement is empty
	if ( (u != NULL)&&(u->all2s) ) {
		return result;
	}

	// Second Special Case
	// given function is unate
	if (u == NULL) {
		result = UnateComplement(fd);
		return result;
	}

	// Third Special Case
	// The function contains a comlumn of either all 0s or all 1s
	// we use Demorgan's Law
	Cube c(fd->inputs());
	c.setCube(u->mapa->getCube());
	string myEmptyCube;
	myEmptyCube.assign(fd->inputs(), '-');
	if (c.getCube() != myEmptyCube) {
		SoPLA * pom = new SoPLA(fd);
		if (pom == NULL) throw RuntimeException("SingleOutputComplement: Not enough memory.");
		pom->AppendTerm(c);
		
		SoPLA * pom_res = UnateComplement(pom);
		result->Splice(*pom_res);

		delete pom_res;
		pom_res = NULL;
		delete pom;
		pom = NULL;
		
		pom = (*fd).Cofactor(c);
		fd->clearData();
		fd->Splice(*pom);
		delete pom;
	}

	// other posibilities
	// first choosing the splitting variable,
	// then using recursive calling SingleOutputComplement
	// finally calling MergeWithContainment	
	unsigned int j = u->split_var;

	Cube xj(fd->inputs());
	Cube xj_non(fd->inputs());
	xj.assign(DC);
	xj.setLit_1(j);
	xj_non.assign(DC);
	xj_non.setLit_0(j);
	
	SoPLA* cc1 = (*fd).Cofactor(xj);
	SoPLA* cc2 = (*fd).Cofactor(xj_non);
	
	SoPLA * cc1_non = SingleOutputComplement(cc1);
	SoPLA * cc2_non = SingleOutputComplement(cc2);

	SoPLA * x = MergeWithContainment(cc2_non, cc1_non, j);  	
	x->Splice(*result);

	delete u;

	delete result;

	delete cc1_non;
	delete cc2_non;

	delete cc1;
	delete cc2;

	return x;
}

// computes PLA complement as an iteration 
// over all single-output funcions
PLA * Complement::PLAComplement( const PLA * pla ) const
{
	if (pla == NULL)
		throw RuntimeException("PLAComplement: Argument cannot be NULL.");

	PLA * result;					// the result;
	SoPLA * fd;						// ONset + DCset of a single output function
	unsigned int m;							// the number of output functions
	SoPLA * hlp;					// the result of SingleOutputComplement function
	CubeList::const_iterator ti;
	unsigned int i;

	if ( !(pla->getType().f && !pla->getType().r) ) throw RuntimeException("Complement: PLA type not supported");

	result = new PLA(pla->inputs(), pla->outputs());
	if (result == NULL) throw RuntimeException("Complement: Not enough memory.");
	m = pla->outputs();			
	for ( i = 0; i < m; i++ )		    // complementation of each single output function
	{
	   fd = pla->ToSoPLA(i, true);  	
	   hlp = SingleOutputComplement(fd);
		
	   string my_output;
	   my_output.assign(pla->outputs(), '~');
	   Cube pom(pla->outputs());
	   pom.setCube(my_output);
	   pom.setLit_1(i);

	   // add the single-output complement to the result
	   for ( ti = hlp->getConstDataRef().begin(); ti != hlp->getConstDataRef().end(); ++ti )
	   	   result->AppendTerm(ti->getCube(), pom.getCube()); 

	   delete fd;
	   delete hlp;
	   hlp = fd = NULL;
	}

	// search for shared terms and merge them
	if (merge_shared) result->MergeTerms();

	result->setType("f");

	return result;
}

SoPLA * Complement::PLAComplement(const SoPLA * pla) const
{
	SoPLA *res, *res1;
	SoPLA placopy;

	if (pla == NULL)
		throw RuntimeException("PLAComplement: Argument cannot be NULL.");

    placopy = *pla;
	res = SingleOutputComplement(&placopy);
	if ( res->getTerms() == 0 ) {
		res1 = new SoPLA(1, false);
		res1->AppendTerm("-");
		res1->setName(res->getName());
		delete res;
		return res1;
	} else return res;
}

/* implementation of nested classes *************************************************************************/
/************************************************************************************************************/

Complement::MultiResult::MultiResult( Cube * c, const unsigned int s, const bool all2s)
{
	mapa = c;
	split_var = s;
	this->all2s = all2s;
}

Complement::MultiResult::~MultiResult()
{
	delete mapa;
} 
