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

#include "stdafx.h"

using namespace std;

#ifdef _MSC_VER
#pragma hdrstop
#endif

#include "myExcept.h"
#include "bitvector.h"
#include "PLA_type.h"
#include "Cube.h"
#include "SoPLA.h"
//---------------------------------------------------------------------------


SoPLA::SoPLA(void)
{
	inputs = 0;
	indexed = false;
	type = true;
	DFF = false;
}

SoPLA::SoPLA( const unsigned int iv )
{
	inputs = iv;
	indexed = false;
	type = true;
	DFF = false;
}

SoPLA::SoPLA( const SoPLA *_pla )
{
	inputs = _pla->inputs;
	inames = _pla->inames;
	name = _pla->name;
	type = _pla->type;
	DFF = _pla->DFF;
	indexed = false;
}

SoPLA::SoPLA( const unsigned int iv, bool _type )
{
	inputs = iv;
	indexed = false;
	type = _type;
	DFF = false;
}

void SoPLA::clear(void)
// Clears all the PLA, deallocates all the structures
{
	data.clear();
    inames.clear();
	indexed = false;
}

SoPLA::~SoPLA(void)
{
}

void SoPLA::SetInputs( const int i )
{
	inputs = i;
}

list<Cube>::iterator SoPLA::AppendTerm( const Cube &t )
// Creates and appends the term. Returns the iterator pointing to this term
{
	list<Cube>::iterator ti;

	if ( inputs == 0 ) inputs = t.GetIMSize();
	else if ( inputs != t.GetIMSize() ) throw RuntimeException( "SoPLA::AppendTerm: incompatible terms" );
	data.push_back(t);
	ti = data.end();
	ti--;
	return ti;
}

list<Cube>::iterator SoPLA::AppendTerm( const string s )
// Creates and appends the term. Returns the iterator pointing to this term
{
	Cube t;

	t.SetIM(s);
	return AppendTerm(t);
}

void SoPLA::AppendPLA( const SoPLA &_pla )
// appends a given PLA to this. The argument PLA is copied
{
	CubeList::const_iterator ti;

	for ( ti = _pla.data.begin(); ti != _pla.data.end(); ti++ )
		AppendTerm(*ti);
}

void SoPLA::Splice( SoPLA &_pla )
// appends a given PLA to this and clears the argument pla
{
	data.splice(data.end(), _pla.data);
}

void SoPLA::Splice( CubeList &terms )
// appends a term list to this and clears the argument pla
{
	data.splice(data.end(), terms);
}

string SoPLA::GetIName(const unsigned int i) const
{
	if ( inames.empty() ) return "";
	if ( i >= inputs ) throw RuntimeException( "SoPLA::SetIName: Incorrect index" );
	else return inames[i];
}

void SoPLA::SetIName(const unsigned int i, const string name)
{
	if ( inames.empty() ) inames.resize( inputs, "" );
	if ( i >= inputs ) throw RuntimeException( "SoPLA::SetIName: Incorrect index" );
	inames[i] = name;
}

bool SoPLA::empty(void) const
{
	return ( data.size() == 0 );
}

void SoPLA::Reindex(void)
// Reindexes the PLA, if not indexed
{
    list<Cube>::iterator ti;
    unsigned int i = 0;

    if ( indexed ) return;
	indexterm.clear();
	for ( ti = data.begin(); ti != data.end(); ti++ ) {
        indexterm.push_back(ti);
		(*ti).SetIndex(i++);
	}
    indexed = true;
}

list<Cube>::iterator SoPLA::GetTerm( const unsigned int i )
// Returns an iterator to an indexed term. If not indexed, Reindex is called
{
	if ( !indexed ) Reindex();
	return indexterm[i];
}

list<Cube>::iterator SoPLA::DeleteTerm(const list<Cube>::iterator ti)
// Deletes the term pointed by iterator. Returns an iterator to the following term
{
	indexed = false;
    return data.erase(ti);
}

void SoPLA::RemoveAbsorbed(void)
// Removes absorbed terms
{
    CubeList::iterator ti, ti1;
	bool deleted;

	if ( data.size() < 2 ) return;
	ti = data.begin();
	do {
		ti1 = ti;
		ti1++;
		deleted = false;
		while ( ti1 != data.end() ) {
			if ( (*ti).Contains( *ti1 ) ) ti1 = DeleteTerm(ti1);
			else if (( *ti1).Contains( *ti ) ) {
				ti = DeleteTerm(ti);
				deleted = true;
				break;
			} else ti1++;
		}
		if ( !deleted ) ti++;
	} while ( ti != data.end() );
}

unsigned int SoPLA::GetLiterals(void)
// Returns the number of PLA literals
{
    unsigned int l = 0;
    list<Cube>::iterator ti;

    for ( ti = data.begin(); ti != data.end(); ti++ )
        l += inputs-(*ti).GetDim();
    return l;
}

unsigned int SoPLA::GetGEs(void)
// Returns GEs*2
{
	unsigned int ge = 0;
	list<Cube>::iterator ti;
	
	for ( ti = data.begin(); ti != data.end(); ti++ )
		if ( inputs-(*ti).GetDim() > 1 ) ge += inputs-(*ti).GetDim();
	ge += GetTerms();
	if ( GetTerms() == 1 ) ge--;		// no OR gate
	if ( GetTerms() == 1 && inputs == 1 && GetData()->front().GetDim() == 0 ) ge++;  // inverter or buffer
	return ge;
}

void SoPLA::RemoveAllDCCols(void)
{
	list<Cube>::iterator ti;
	unsigned int i;
	vector<bool> alldc;
	string new_IM;
	vector<string> new_names;
	unsigned int cnt = 0;

	alldc.resize( inputs, false );
	for ( i = 0; i < inputs; i++ ) {
		for ( ti = data.begin(); ti != data.end(); ti++ )
			if ( (*ti).GetIM(i) != '-' ) break;
		if ( ti == data.end() ) {
			alldc[i] = true;
			cnt++;
		}
	}
	if ( cnt == 0 ) return;
	for ( ti = data.begin(); ti != data.end(); ti++ ) {
		new_IM.clear();
		for ( i = 0; i < inputs; i++ )
			if ( !alldc[i] ) new_IM.push_back( (*ti).GetIM(i) );
		(*ti).ClearIM();
		(*ti).SetIM(new_IM);
		if ( (*ti).dim != -1 ) (*ti).dim -= cnt;
	}
	new_names.clear();
	for ( i = 0; i < inputs; i++ )
		if ( !alldc[i] ) new_names.push_back( inames[i] );
	inames = new_names;
	SetInputs(GetInputs()-cnt);
}
