//---------------------------------------------------------------------------
#include "stdafx.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

#include "myExcept.h"
#include "bitvector.h"
#include "Cube.h"
#include "Term.h"

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

Term::Term(void):
	Cube()
{
	valid = true;
}

Term::Term( const unsigned int iv, const unsigned int ov ):
	Cube(iv)
{
	OM.assign( ov, '~' );
	OM_data.assign( ov, UNDEF );
	//OM_data.second.assign( ov, true );
	origin = 0;
	valid = true;
}

void Term::init( const unsigned int iv, const unsigned int ov )
{
	Cube::init(iv);
	OM.assign( ov, '~' );
	OM_data.assign( ov, UNDEF );
	//OM_data.second.assign( ov, true );
	valid = true;
}

/*Term::~Term(void)
{
}*/

void Term::clear(void)
// Clears the term structure
{
	Cube::clear();
	OM.clear();
	OM_data.clear();
	//OM_data.second.clear();
}

void Term::setOM( const unsigned int pos, const char val )
// Sets a given output value
{
    if ( pos >= outputs() ) throw RuntimeException( "Term::setOM: Index out of range" );
    if ( val != '0' && val != '1' && val != '-' && val != '~' ) throw RuntimeException( "Term::setOM: Invalid value" );
	switch (val) {
		case '0':
			setOM_0(pos);
			break;
		case '1':
			setOM_1(pos);
			break;
		case '-':
			setOM_dc(pos);
			break;
		case '~':
			setOM_undef(pos);
			break;
		default: throw RuntimeException( "Term::setOM: Invalid value" );
	}
}

void Term::setOM( const string val )
// Sets the output
{
	unsigned int i;
	
	if ( val.size() != outputs() && outputs() != 0 ) throw RuntimeException( "Term::setOM: Wrong OM size" );
	if ( outputs() == 0 ) {
		OM.resize( val.size() );
		OM_data.resize( val.size() );
		//OM_data.second.resize( val.size() );
	}
	for ( i = 0; i < outputs(); i++ ) setOM(i, val[i]);
}

unsigned int Term::getOC(void) const
// Returns the output cost.
{
    unsigned int j, oc = 0;
    for ( j = 0; j < outputs(); j++ )
        if ( isOM_1(j) ) oc++;
    return oc;
}

bool Term::intersects( const Term& t ) const
/*
    Returns: do the terms intersect?
    Checks also outputs
*/
{
    unsigned int i;

    if ( t.outputs() != this->outputs() ) throw RuntimeException("Term::intersects: Comparing incompatible terms");
           // do the outputs intersect?
    for ( i = 0; i < outputs(); i++ )
        if ( this->isOM_1(i) && t.isOM_1(i) ) break;
    if ( i == outputs() ) return false;
           // do the intputs intersect?
	return ( Cube::intersects(t) );
}

int Term::intersectSize( const Term& t ) const
/*
    Returns: do the terms intersect?
    Checks also outputs
*/
{
    unsigned int i;

    if ( t.outputs() != this->outputs() ) throw RuntimeException("Term::intersects: Comparing incompatible terms");
           // do the outputs intersect?
    for ( i = 0; i < outputs(); i++ )
        if ( this->isOM_1(i) && t.isOM_1(i) ) break;
    if ( i == outputs() ) return -1;
           // do the intputs intersect?
    return ( Cube::intersectSize(t.IM) );
}

unsigned int Term::Relation( const Term& t ) const
/*
    Bit 0: non-empty IM intersection
	Bit 1: t.IM is contained
	Bit 2: IMs are equal
    Bit 3: non-empty OM intersection
	Bit 4: t.OM is contained
	Bit 5: OMs are equal
*/
{
    unsigned int i;
	int res = 63-8;

	if ( t.inputs() != this->inputs() || t.outputs() != this->outputs() )
     throw RuntimeException("Term:intersects:Comparing incompatible terms");

	for ( i = 0; i < outputs(); i++ ) {
        if ( !this->isOM_1(i) && t.isOM_1(i) ) res &= (63-16);   // t is not contained
		if ( this->isOM_1(i) && t.isOM_1(i) ) res |= 8;		// non-empty intersection
		if ( this->getOM(i) != t.getOM(i) ) res &= (63-32);		// not equal
	}
	for ( i = 0; i < inputs(); i++ ) {
		if ( (this->isLit_0(i) && t.isLit_1(i) ) || (this->isLit_1(i) && t.isLit_0(i) ) ) {
			res &= (63-7);   // 0/1 conflict. No intersection
			break;
		}
		if ( !this->isLit_dc(i) && t.isLit_dc(i) ) res &= (63-2);		// not contained
		if ( this->getLit(i) != t.getLit(i) ) res &= (63-4);		// not equal
	}
    return ( res );
}

unsigned int Term::Relation2( const Term& t ) const
/*
    0: non-empty IM intersection, terminates
	Bit 1: t.IM is contained
	Bit 2: IMs are equal
    Bit 3: non-empty OM intersection
	Bit 4: t.OM is contained
	Bit 5: OMs are equal
*/
{
    unsigned int i;
	int res = 63-8;

	if ( t.inputs() != this->inputs() || t.outputs() != this->outputs() )
		throw RuntimeException("Term::intersects:Comparing incompatible terms");

	for ( i = 0; i < inputs(); i++ ) {
		if ( (this->isLit_0(i) && t.isLit_1(i) ) || (this->isLit_1(i) && t.isLit_0(i) ) ) {
			res = 0;   // 0/1 conflict. No intersection. Terminate
			break;
		}
		if ( !this->isLit_dc(i) && t.isLit_dc(i) ) res &= (63-2);		// not contained
		if ( this->getLit(i) != t.getLit(i) ) res &= (63-4);		// not equal
	}
	if (res != 0) 
	  for ( i = 0; i < outputs(); i++ ) {
        if ( !this->isOM_1(i) && t.isOM_1(i) ) res &= (63-16);   // t is not contained
		if ( this->isOM_1(i) && t.isOM_1(i) ) res |= 8;		// non-empty intersection
		if ( this->getOM(i) != t.getOM(i) ) res &= (63-32);		// not equal
	  }
    return res;
}

bool Term::intersects( const Term& t, const unsigned int _ov ) const
/*
    Returns: do the terms intersect?
    Checks given output only
*/
{
    if ( t.outputs() != this->outputs() ) throw RuntimeException("Term::intersects: Comparing incompatible terms");
           // do the outputs intersect?
    if ( !this->isOM_1(_ov) || !t.isOM_1(_ov) ) return false;
           // do the intputs intersect?
	return ( Cube::intersects(t) );
}

bool Term::contains( const Term& t ) const
/*
    Returns: does it fully contain t?
*/
{
    unsigned int i;

    if ( t.outputs() != this->outputs() ) throw RuntimeException("Term::contains: Comparing incompatible terms");
	for ( i = 0; i < outputs(); i++ )
        if ( !this->isOM_undef(i) && t.getOM(i) != this->getOM(i) ) return false;
	return Cube::contains(t);
}

void Term::Merge( const Term& t )
/*
    Merge the terms by output matrices
    !! the terms are considered to have equal IM
    !! only for fd types
*/
{
    unsigned int i;

    for ( i = 0; i < outputs(); i++ )
        if ( t.isOM_1(i) ) this->setOM_1(i);
}

void Term::setOM_0( const unsigned int pos )
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::setOM_0: Index out of range" );
	OM[pos] = '0';
	OM_data.setbit(pos, ZERO);
	//OM_data.second.setbit(pos, false);
}

void Term::setOM_1( const unsigned int pos )
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::setOM_1: Index out of range" );
	OM[pos] = '1';
	OM_data.setbit(pos, ONE);
	//OM_data.second.setbit(pos, false);
}

void Term::setOM_dc( const unsigned int pos )
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::setOM_dc: Index out of range" );
	OM[pos] = '-';
	OM_data.setbit(pos, DC);
	//OM_data.second.setbit(pos, true);
}

void Term::setOM_undef( const unsigned int pos )
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::setOM_undef: Index out of range" );
	OM[pos] = '~';
	OM_data.setbit(pos, UNDEF);
	//OM_data.second.setbit(pos, true);
}

bool Term::isOM_0( const unsigned int pos ) const
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::isOM_0: Index out of range" );
	return ( OM[pos] == '0' );
}

bool Term::isOM_1( const unsigned int pos ) const
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::isOM_1: Index out of range" );
	return ( OM[pos] == '1' );
}

bool Term::isOM_dc( const unsigned int pos ) const
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::isOM_dc: Index out of range" );
	return ( OM[pos] == '-' );
}

bool Term::isOM_undef( const unsigned int pos ) const
{
	if ( pos >= outputs() ) throw RuntimeException( "Term::isOM_undef: Index out of range" );
	return ( OM[pos] == '~' );
}

void Term::assignOM( Literal val )
// assigns a value to all vector items
{
	unsigned int n;

	n = OM_data.size();
	switch ( val ) {
		case ONE: //this->OM_data.assign(n, ONE);
				  //this->OM_data.second.assign(n, false);
				  OM.assign(n, '1');
				  break;
		case ZERO: //this->OM_data.assign(n, ZERO);
				  //this->OM_data.second.assign(n, false);
				  OM.assign(n, '0');
				  break;
		case DC:  //this->OM_data.assign(n, DC);
				  //this->OM_data.second.assign(n, true);
				  OM.assign(n, '-');
				  break;
		case UNDEF:  //this->OM_data.assign(n, UNDEF);
				  //this->OM_data.second.assign(n, true);
				  OM.assign(n, '~');
				  break;
		default: throw RuntimeException( "Cube::assign: incorrect literal value" );
	}
}

void Term::assignOM( unsigned int size, Literal val )
{
	OM.resize(size);
	OM_data.assign(size, val);
	//OM_data.second.resize(size);
	this->assignOM(val);
}
