#include "stdafx.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

#include "kernelincludes.h"

#include "PLA_operations.h"

PLA *PLA_op::JoinPLAsByInputs( PLA &pla1, PLA &pla2 )
// Joins two PLAs. Input matrices are merged, the resulting output matrix is extended
{
	PLA *res;
	TermList::iterator ti, ti2;
	Term t;
	unsigned int i;
	string om;

	if ( pla1.GetInputs() != pla2.GetInputs() || pla1.GetType() != pla2.GetType() ) throw RuntimeException( "PLA_operations::JoinPLAsByInputs: incompatible PLAs" );

	res = new PLA(pla1.GetInputs(), pla1.GetOutputs()*2, pla1.GetType());
	if ( res == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
	for ( ti = pla1.GetData()->begin(); ti != pla1.GetData()->end(); ti++ ) {
		om = (*ti).GetOM();
		for ( i = 0; i < pla2.GetOutputs(); i++ ) om += '~';
		t.SetIM((*ti).GetIM());
		t.SetOM(om);
		res->AppendTerm(t);
	}
	for ( ti = pla2.GetData()->begin(); ti != pla2.GetData()->end(); ti++ ) {
		ti2 = res->FindTerm((*ti).GetIM());
		if ( ti2 == res->GetData()->end() ) {
			om.clear();
			for ( i = 0; i < pla1.GetOutputs(); i++ ) om += '~';
			om += (*ti).GetOM();
			t.SetIM((*ti).GetIM());
			t.SetOM(om);
			res->AppendTerm(t);
		}
		else 
			for ( i = 0; i < pla2.GetOutputs(); i++ )
				(*ti2).SetOM(pla1.GetOutputs()+i, (*ti).GetOM(i));
	}
	return res;
}

// -----------------------------------------------------------------

SoPLA *PLA_op::TermSharp( const Cube& t1, const Cube& t2 )
/* performs t1 # t2
	I.e., returns a list of terms covered by t1 and not not by t2
*/
{
	unsigned int i;
	SoPLA *res;		// the result
	Cube newterm;
	string om;

	if ( t1.GetInputs() != t2.GetInputs() ) throw RuntimeException( "TermSharp: Different term sizes" );
	res = new SoPLA( t1.GetInputs() );
	if ( res == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
	if ( !t1.Intersects(t2) ) {
		res->AppendTerm(t1);
		return res;
	}

	for ( i = 0; i < t1.GetIMSize(); i++ )
		if ( t1.GetIM(i) == '-' && t2.GetIM(i) != '-' ) {
			newterm = t1;
			newterm.SetIM(i, 1-(t2.GetIM(i)-'0')+'0');
			res->AppendTerm(newterm);
		}
	return res;
}

PLA *PLA_op::TermSharp( const Term& t1, const Term& t2 )
/* performs t1 # t2
	I.e., returns a list of terms covered by t1 and not not by t2
	Resolves outputs as well
*/
{
	unsigned int i, j;
	PLA *res;		// the result
	Term newterm;
	bool copies;
	string om;

	if ( t1.GetInputs() != t2.GetInputs() || t1.GetOutputs() != t2.GetOutputs() ) throw RuntimeException( "TermSharp: Different term sizes" );
	res = new PLA( t1.GetInputs(), t1.GetOutputs(), PLA_type("f") );
	if ( res == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
	if ( !t1.Intersects(t2) ) {
		res->AppendTerm(t1);
		return res;
	}
	
	copies = false;			// if contained in t1 and not in t2, copy it to the result for the resp. output variable
	om.assign( t1.GetOMSize(), '~' );
	for ( j = 0; j < t1.GetOMSize(); j++ )
		if ( t1.GetOM(j) == '1' && t2.GetOM(j) != '1' ) {
			copies = true;
			om[j] = '1';
		}
	if ( copies ) {
		newterm = t1;
		newterm.SetOM(om);
		res->AppendTerm(newterm);
	}

	for ( i = 0; i < t1.GetIMSize(); i++ )
		if ( t1.GetIM(i) == '-' && t2.GetIM(i) != '-' ) {
			newterm = t1;
			newterm.SetIM(i, 1-(t2.GetIM(i)-'0')+'0');
			res->AppendTerm(newterm);
		}
	return res;
}

PLA *PLA_op::PLATermSharp( PLA &pla, const Term &t )
/* returns PLA obtained as pla # t
	I.e., in the resulting pla are terms in pla and not in t
*/
{
	PLA *res;
	TermList::const_iterator ti;
	PLA *terms;

	res = new PLA(&pla);
	if ( res == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
	for ( ti = pla.GetData()->begin(); ti != pla.GetData()->end(); ti++ ) {
		terms = TermSharp(*ti, t);
		res->Splice(*terms);
		delete terms;
	}
	return res;
}

SoPLA *PLA_op::PLATermSharp( SoPLA &pla, const Cube &t )
/* returns PLA obtained as pla # t
	I.e., in the resulting pla are terms in pla and not in t
*/
{
	SoPLA *res;
	CubeList::const_iterator ti;
	SoPLA *terms;

	res = new SoPLA(&pla);
	if ( res == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
	for ( ti = pla.GetData()->begin(); ti != pla.GetData()->end(); ti++ ) {
		terms = TermSharp(*ti, t);
		res->Splice(*terms);
		delete terms;
	}
	return res;
}

PLA *PLA_op::PLASharp( PLA &pla1, PLA &pla2 )
/* // Returns PLA obtained as pla1 # pla2
	I.e., in the resulting pla are terms that are in pla1 and not in pla2
*/
{
	PLA *res, *res2 = NULL;
	TermList::const_iterator ti, ti1;
	TermList terms;

	if ( pla1.GetTerms() == 0 ) return new PLA(&pla1);	// empty PLA
	if ( pla2.GetTerms() == 0 ) return new PLA(pla1);	// copying constructor

	res = &pla1;
	res->SetType(PLA_type("f"));
	for ( ti1 = pla2.GetData()->begin(); ti1 != pla2.GetData()->end(); ti1++ ) {
		res2 = PLATermSharp(*res, *ti1);
		if ( res != &pla1 ) delete res;
		res = res2;
		res->RemoveAbsorbed();
//		res->MergeTerms();
//		res->MergeNeighbors();
	}
	return res2;
}

SoPLA *PLA_op::PLASharp( SoPLA &pla1, SoPLA &pla2 )
/* // Returns PLA obtained as pla1 # pla2
	I.e., in the resulting pla are terms that are in pla1 and not in pla2
*/
{
	SoPLA *res, *res2 = NULL;
	CubeList::const_iterator ti, ti1;
	CubeList terms;

	if ( pla1.GetTerms() == 0 ) return new SoPLA(&pla1);	// empty PLA
	if ( pla2.GetTerms() == 0 ) return new SoPLA(pla1);	// copying constructor

	res = &pla1;
	for ( ti1 = pla2.GetData()->begin(); ti1 != pla2.GetData()->end(); ti1++ ) {
		res2 = PLATermSharp(*res, *ti1);
		if ( res != &pla1 ) delete res;
		res = res2;
		res->RemoveAbsorbed();
//		res->MergeTerms();
//		res->MergeNeighbors();
	}
	return res2;
}

PLA *PLA_op::PLAComplement( PLA &pla )
//	Returns a complement of a PLA, computed by Sharp
{
	PLA *tautology;
	PLA *res;
	string iv, ov;
	TermList::iterator ti;

	if ( pla.GetType().r && !pla.GetType().f && pla.GetOutputs() == 1 ) {	// just temporary, for Blif r nodes
		res = new PLA(&pla);
		res->SetType(PLA_type("f"));
		for ( ti = pla.GetData()->begin(); ti != pla.GetData()->end(); ti++ ) {
			res->AppendTerm((*ti));
			res->GetData()->back().SetOM("1");
		}
	}
	else {
		tautology = new PLA(&pla);
		if ( tautology == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
		iv.assign(pla.GetInputs(), '-');
		ov.assign(pla.GetOutputs(), '1');
		tautology->AppendTerm(iv, ov);

		res = PLASharp(*tautology, pla );
		delete tautology;
	}
	return res;
}

SoPLA *PLA_op::PLAComplement( SoPLA &pla )
//	Returns a complement of a PLA, computed by Sharp
{
	SoPLA *tautology;
	SoPLA *res;
	string iv;

	tautology = new SoPLA(&pla);
	if ( tautology == NULL) throw RuntimeException( "PLA_operations: Not enough memory" );
	iv.assign(pla.GetInputs(), '-');
	tautology->AppendTerm(iv);

	res = PLASharp(*tautology, pla );
	delete tautology;
	return res;
}

SoPLA *PLA_op::Cofactor( SoPLA &pla, const Cube &t )
// Computes a cofactor of pla wrt. t. The resulting PLA is of the same dimensions as pla
{
	SoPLA *res;
	CubeList::iterator ti;
	Cube newterm;
	unsigned int i;

	if ( pla.GetInputs() != t.GetIMSize() ) throw RuntimeException( "PLA_operations::Cofactor: PLA and the term are incompatible" );
	// if ( pla.GetOutputs() != t.GetOMSize() ) throw RuntimeException( "PLA_operations::Cofactor: PLA and the term are incompatible" );
//	i = t.GetDim();
	res = new SoPLA(&pla);
	for ( ti = pla.GetData()->begin(); ti != pla.GetData()->end(); ti++ ) {
		newterm = (*ti);
		for ( i = 0; i < pla.GetInputs(); i++ )
			if ( t.GetIM(i) != '-' ) {
				if ( t.GetIM(i) != (*ti).GetIM(i) && (*ti).GetIM(i) != '-' ) break;
				else newterm.SetIM(i, '-');
			}
		if ( i == pla.GetInputs() ) res->AppendTerm(newterm);
	}
	return res;
}
