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

#include "stdafx.h"

using namespace std;

#ifdef _MSC_VER
#pragma hdrstop
#endif

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

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

Cube::Cube(void)
{
    dim = -1;
	unwanted = false;
}

Cube::Cube( const unsigned int iv )
{
	IM.assign(iv, '-');
	IM_data.assign( iv, DC );
	//IM_data.second.assign( iv, true );

    dim = iv;
	unwanted = false;
}

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

void Cube::clear(void)
// Clears the Cube structure
{
	IM.clear();
	IM_data.clear();
	//IM_data.second.clear();
	SO_CoverVector.clear();
	CoverVector.clear();
}

void Cube::init( const unsigned int iv )
{
	IM.assign( iv, '-' );
	IM_data.assign( iv, DC );
	//IM_data.second.assign( iv, true );
	dim = iv;
	unwanted = false;
	SO_CoverVector.clear();
	CoverVector.clear();
}

void Cube::setLit_0( const unsigned int pos )
{
	if ( pos >= this->size() ) throw RuntimeException( "Cube::setLit_0: Index out of range" );
    if ( dim != -1 && isLit_dc(pos) ) dim--;
	IM[pos] = '0';
	IM_data.setbit(pos, ZERO);
	//IM_data.second.setbit(pos, false);
}

void Cube::setLit_1( const unsigned int pos )
{
	if ( pos >= this->size() ) throw RuntimeException( "Cube::setLit_1: Index out of range" );
    if ( dim != -1 && isLit_dc(pos) ) dim--;
	IM[pos] = '1';
	IM_data.setbit(pos, ONE);
	//IM_data.second.setbit(pos, false);
}

void Cube::setLit_dc( const unsigned int pos )
{
	if ( pos >= this->size() ) throw RuntimeException( "Cube::setLit_dc: Index out of range" );
    if ( dim != -1 && !isLit_dc(pos) ) dim++;
	IM[pos] = '-';
	IM_data.setbit(pos, DC);
	//IM_data.second.setbit(pos, true);
}

bool Cube::isLit_0( const unsigned int pos ) const
{
	//return ( !IM_data.first[pos] && !IM_data.second[pos] );
	return IM_data[pos] == ZERO;
}

bool Cube::isLit_1( const unsigned int pos ) const
{
	//return ( IM_data.first[pos] && !IM_data.second[pos] );
	return IM_data[pos] == ONE;
}

bool Cube::isLit_dc( const unsigned int pos ) const
{
	//return ( !IM_data.first[pos] && IM_data.second[pos] );
	return IM_data[pos] == DC;
}

void Cube::setLit( const unsigned int pos, const Literal val  )
{
	if ( pos >= this->size() ) throw RuntimeException( "Cube::setLit: Index out of range" );
    if ( dim != -1 && isLit_dc(pos) && val != DC ) dim--;
    if ( dim != -1 && !isLit_dc(pos) && val == DC ) dim++;
	switch ( val ) {
		case ZERO:
			setLit_0(pos);
			break;
		case ONE:
			setLit_1(pos);
			break;
		case DC:
			setLit_dc(pos);
			break;
		default: throw RuntimeException( "Cube::setLit: Wrong IM value" );
			break;
	}
}

void Cube::setLit( const unsigned int pos, const char val )
// Sets a given input
{
	if ( pos >= this->size() ) throw RuntimeException( "Cube::setLit: Index out of range" );
    if ( dim != -1 && isLit_dc(pos) && val != '-' ) dim--;
    if ( dim != -1 && !isLit_dc(pos) && val == '-' ) dim++;
    IM[pos] = val;
	switch ( val ) {
		case '0':
			this->setLit_0(pos);
			break;
		case '1':
			this->setLit_1(pos);
			break;
		case '-':
			this->setLit_dc(pos);
			break;
		default:
			throw RuntimeException( "Cube::setLit: Invalid value" );
	}
}

void Cube::setCube( const string val )
{
	unsigned int i;

	if ( val.size() != this->size() && this->size() != 0 ) throw RuntimeException( "Cube::setCube: Wrong IM size" );
	IM = val;
	if ( this->size() == 0 ) {
		IM_data.resize(val.size());
		//IM_data.second.resize(val.size());
	}
	dim = -1;
	for ( i = 0; i < val.size(); i++ ) this->setLit( i, val[i] );	
}

void Cube::setCube( const vector<Literal> val )
{
	unsigned int i;

	if ( val.size() != this->size() && this->size() != 0 ) throw RuntimeException( "Cube::setCube: Wrong IM size" );
	if ( this->size() == 0 ) {
		IM_data.resize(val.size());
		//IM_data.second.resize(val.size());
		IM.resize(val.size());
	}
	dim = -1;
	for ( i = 0; i < val.size(); i++ ) this->setLit( i, val[i] );	
}

Literal Cube::getLit2( const unsigned int pos ) const
{
	/*if ( this->isLit_0(pos) ) return ZERO;
	else if ( this->isLit_1(pos) ) return ONE;
	else if ( this->isLit_dc(pos) ) return DC;
	else throw RuntimeException( "Cube::getLit2: Not allowed literal value" );*/
	return IM_data[pos];
}

vector<Literal> Cube::getCube2(void) const
{
	vector<Literal> res;
	unsigned int i;

	for ( i = 0; i < size(); i++ )
		res.push_back( getLit2(i) );
	return res;
}

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

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

void Cube::assign( unsigned int size, Literal val )
// assigns a value to all vector items
{
	IM.resize(size);
	IM_data.resize(size);
	//IM_data.second.resize(size);
	this->assign(val);
}

unsigned int Cube::getDim(void)
// Returns term dimension. If it is not already computed, computes it
{
    unsigned int i, j;

    if ( dim != -1 ) return dim;
    i = 0;
	for ( j = 0; j < this->size(); j++ )
        if ( isLit_dc(j) ) i++;
    dim = i;
    return i;
}

bool Cube::intersects( const string &s ) const
/*
    Returns: do the terms intersect?
	Deprecarted.
*/
{
    unsigned int i;

	if ( s.size() != this->size() ) throw RuntimeException("Cube::intersects: Comparing incompatible terms");
    for ( i = 0; i < IM.size(); i++ )
        if ( this->IM[i] != s[i] && this->IM[i] != '-' && s[i] != '-' ) return false;
    return true;
}

/*
bool Cube::intersects( const Cube &t ) const
// does it intersect a cube? 
{
	bitvector bv = this->IM_data.first;

	bv ^= ~t.IM_data.first;
	bv |= t.IM_data.second;
	bv |= this->IM_data.second;
	return bv.isOne();
}
*/

bool Cube::intersects( const Cube &t ) const		// this is much faster
/*
    Returns: do the terms intersect?
*/
{
    unsigned int i;

	if ( t.size() != this->size() ) throw RuntimeException("Cube::intersects: Comparing incompatible terms");
    for ( i = 0; i < t.size(); i++ )
		if ( this->getLit2(i) != t.getLit2(i) && !this->isLit_dc(i) && !t.isLit_dc(i) ) return false;
    return true;
}

int Cube::intersectSize( const string &s ) const
/*
    Returns the intersection size (dimension)
	Deprecated.
*/
{
    unsigned int i;
	int j = 0;

	if ( s.size() != this->IM.size() ) throw RuntimeException("Cube::intersects: Comparing incompatible terms");
    for ( i = 0; i < IM.size(); i++ )
        if ( this->IM[i] != s[i] && this->IM[i] != '-' && s[i] != '-' ) return -1;
		else if ( (this->IM[i] == '-' && s[i] != '-') || (this->IM[i] != '-' && s[i] == '-') ) j++;
    return j;
}

/*int Cube::intersectSize( const Cube &t ) const
/*
    Returns the intersection size (dimension)

{
    unsigned int i;
	int j = 0;

	bitvector bv = this->IM_data.first;
	
	bv ^= ~t.IM_data.first;
	bv |= t.IM_data.second;
	bv |= this->IM_data.second;
	if ( !bv.isOne() ) return -1;
	bv = this->IM_data.second & t.IM_data.second;
	for ( i = 0; i < this->size(); i++ )
		if ( bv[i] ) j++;
	return j;
}*/

Cube Cube::intersection( const string& s ) const
/*
    Returns the intersection of the terms
    The terms must have an intersection
	Deprecated
*/
{
    unsigned int i;
    Cube t1;

    t1 = *this;
    for ( i = 0; i < IM.size(); i++ ) {
		if ( t1.IM[i] != s[i] && t1.IM[i] != '-' && s[i] != '-' ) throw RuntimeException( "Cube::intersection: no intersection" );;
        if ( s[i] == '0' || s[i] == '1' ) t1.IM[i] = s[i];
    }
    return t1;
}

/*Cube Cube::intersection( const Cube &t ) const
/*
    Returns the intersection of the terms
    The terms must have an intersection! intersection check is not performed!

{
    Cube inter;

	// inter.IM_data.first = (~(this->IM_data.second) | t.IM_data.first) & (~(t.IM_data.second) | this->IM_data.first);
	inter.IM_data.first = ~(this->IM_data.second);
	inter.IM_data.first |= t.IM_data.first;
	inter.IM_data.first &= (~(t.IM_data.second) | this->IM_data.first);
	inter.IM_data.second = this->IM_data.second;
	inter.IM_data.second &= t.IM_data.second;
	
	return inter;
}*/

bool Cube::contains( const string &s ) const
/*
    Returns: does it contain t?
	Deprecated
*/
{
    unsigned int i;

    if ( s.size() != this->IM.size() )
		throw RuntimeException("Cube::contains: Comparing incompatible terms");

	for ( i = 0; i < IM.size(); i++ )
        if ( this->IM[i] != s[i] && this->IM[i] != '-' ) return false;
    return true;
}

/*
bool Cube::contains( const Cube &t ) const
//  Returns: does it contain t?
{
	bitvector r = this->IM_data.first;
	r ^= t.IM_data.first;
	r |= t.IM_data.second;
	r &= ~this->IM_data.second;
	return r.isNull();
}
*/

bool Cube::contains( const Cube &t ) const		// this is surprisingly faster
//  Returns: does it contain t?
{
	unsigned int i;

    if ( t.size() != this->size() )
		throw RuntimeException("Cube::contains: Comparing incompatible terms");

	for ( i = 0; i < this->size(); i++ )
		if ( this->getLit2(i) != t.getLit2(i) && !this->isLit_dc(i) ) return false;
    return true;
}

bool Cube::operator== ( const Cube &t ) const
// returns true, if the cubes are equal
{
	return this->IM_data == t.IM_data;
}

//=========== for testing purposes ==============
bool Cube::And( const string &s ) const
{
    unsigned int i;
	bool res = true;

    if ( s.size() != this->IM.size() )
		throw RuntimeException("Cube::contains: Comparing incompatible terms");

	for ( i = 0; i < IM.size(); i++ )
        if ( this->IM[i] == '1' && s[i] != '1' ) res = false;
    return res;
}


bool Cube::And( Cube &t )
{
	//return ( this->IM_data.first & t.IM_data.first ).isOne();
	t.IM_data &= this->IM_data;
	return t.IM_data.isOne();
}
