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

#include "stdafx.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

#include "../../Kernel/kernelincludes.h"

#include "defaults.h"
// #include "TimeStamp.h"
#include "CovMatrix.h"
#include "HeurCover.h"
#include "AuraCover.h"
#include "LagBaBCover.h"
#include "Expand.h"

#include "Cover.h"

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

Cover::Cover( PLA *_source, PLA *_implpool, TermQueue * tq_ie_gom, TermQueue * tq_gom_ra, TermQueue * tq_ir_rabc)
{
    source = _source;
    implpool = _implpool;
	this->tq_ie_gom = tq_ie_gom;
	this->tq_gom_ra = tq_gom_ra;
	this->tq_ir_rabc = tq_ir_rabc;
}

void Cover::GenerateOM(void)
// Generates OM values in the implicant pool (only from void "~" values)
{
    unsigned int i, outputs = implpool->outputs();
    TermList::iterator ti;
	Term t;

    //for ( ti = implpool->getData()->begin(); ti != implpool->getData()->end(); ti++ )
	while (true) {
		tq_ie_gom->pop(t);
		if (t.invalid()) {
			break;
		}
		for (i = 0; i < outputs; i++) {
			if (t.isOM_undef(i)) {
				if (source->isImplicant(t, i))
					t.setOM_1(i);
				else
					t.setOM_0(i);
			}
		}
		tq_gom_ra->push(t);
	}
	/*Term tt;
	tt.invalidate();
	tq3->push(tt);*/
}

void Cover::GenerateCoverVector( Term &t )
/*
	Generates CoverVector structure for a given term (if it is empty)
	It's size is equal to the # of 1's in the source OM (taken from PLA::getOnSet)
*/
{
	vector<bitvector> *SO_covvct;
	CubeList::iterator ti;
	unsigned int o;
	bool covered;		// is the term covered (contained) by t?
	bitvector *covvct;
	
	if ( !t.getCoverVector()->empty() && !t.getSO_CoverVector(0)->empty() ) return;
	
	SO_covvct = t.getSO_CoverVector();
	covvct = t.getCoverVector();
	SO_covvct->resize( source->outputs() );
	for ( o = 0; o < source->outputs(); o++ )
		for ( ti = source->getOnSet(o)->getData()->begin(); ti != source->getOnSet(o)->getData()->end(); ti++ ) {
			covered = t.Cube::contains(ti->getCube());
			if ( covered && t.isOM_1(o) ) {		// covers this output and it is an implicant of o-th function
				covvct->push_back(true);
				(*SO_covvct)[o].push_back(true);
			} else {
				covvct->push_back(false);
				(*SO_covvct)[o].push_back(false);
			}
		}
}

void Cover::GenerateCoverVectors(void)
/*
	Generates CoverVectors for the whole implicant pool
*/
{
	TermList::iterator ti;
	implptrs.reserve(implpool->getDataRef().size() * 2);
	implptrs.clear();
	for (ti = implpool->getDataRef().begin(); ti != implpool->getDataRef().end(); ++ti)
		implptrs.push_back(&(*ti));
	
	int i, n = implptrs.size();
	#pragma omp parallel for default(none) \
		shared(n) private(i)
	for (i = 0; i < n; i++)
		GenerateCoverVector(*implptrs[i]);
}

void Cover::GenerateCoverVectors(PLA &pla)
/*
	Generates CoverVectors for the given PLA
*/
{
	TermList::iterator ti;

	for ( ti = pla.getData()->begin(); ti != pla.getData()->end(); ++ti )
		GenerateCoverVector(*ti);
}



void Cover::RemoveAbsorbedByCover(void)
/*
	By comparing CoverVectors, remove clearly absorbed terms from the pool.
	If same terms are covered by two, the one with smaller dimension is removed.
*/
{
	TermList::iterator ti;
	bool abs1, abs2;				// which one is absorbed
	bool deleted;
	Term t;
	//GenerateCoverVectors();
	while (true) {
		tq_ir_rabc->pop(t);
		if (t.invalid()) break;
		GenerateCoverVector(t);
		deleted = false;
		int n = implptrs.size(), i;
		#pragma omp parallel for default(none) \
			shared(n, deleted, t) private(i, abs1, abs2)
		for (i = 0;	i < n; i++) {
			#pragma omp flush(deleted)
			if (!deleted) {
				if (implptrs[i]->isUnwanted()) continue;
				abs1 = abs2 = true;
				if (!t.getCoverVector()->contains(*implptrs[i]->getCoverVector())) abs2 = false;
				if (!implptrs[i]->getCoverVector()->contains(*t.getCoverVector())) abs1 = false;

				if (abs1 && abs2) {
					if ((t.getDim() >= t.getDim())) {
						implptrs[i]->setUnwanted();
					} else {
						deleted = true;
						#pragma omp flush(deleted)
					}
				} else if (abs1 && !abs2) {
					deleted = true;
					#pragma omp flush(deleted)
				} else if (!abs1 && abs2) {
					implptrs[i]->setUnwanted();
				}
			}
		}
		if (!deleted) {
			ti = implpool->AppendTerm(t);
			implptrs.push_back(&(*ti));
		}
	}
	for (ti = implpool->getData()->begin(); ti != implpool->getData()->end(); ) {
		if (ti->isUnwanted()) {
			ti = implpool->DeleteTerm(ti);
		} else ++ti;
	}
}

PLA *Cover::Run(void)
// Runs the CP
{
	CovMatrix *CM;
	PLA *solution;
	unsigned int i;
	TermList::iterator ti;
	unsigned int rownum;
	list<unsigned int> *sol;
	list<unsigned int>::iterator sol_it;
	float t1;
	ofstream trialfile;

//	TS.start(TS_CP);

	ti = implpool->getData()->begin();		// determine vector size by the first one
	if ( (*ti).getCoverVector()->empty() ) GenerateCoverVectors();
	rownum = (*ti).getCoverVector()->size();
	CM = new CovMatrix( rownum, implpool->getData()->size() );
	if ( CM == NULL) throw RuntimeException( "Cover: Not enough memory" );
	for ( ti = implpool->getData()->begin(); ti != implpool->getData()->end(); ti++ ) {
		//if ( (*ti).getCoverVector()->empty() ) GenerateCoverVector(*ti);
		if ( (*ti).getCoverVector()->size() != rownum ) {
			unsigned a = (*ti).getCoverVector()->size();
			bitvector * bv = (*ti).getCoverVector();
			throw RuntimeException( "Cover::Run:: Bad size of cover vector" );
		}
		for ( i = 0; i < rownum; i++ )
			CM->SetValue(i, implpool->getIndex(ti), (*(*ti).getCoverVector())[i] == ZERO ? false : true);
		CM->SetCost(implpool->getIndex(ti), implpool->inputs() - (*ti).getDim() + 1);
	}
	
	//CM->DumpMatrix( "matrix.dat" );

	trialfile.open("trials.csv", ios::app);
	t1 = GetMyCPUTime();

	sol = Solve(CM);

	trialfile << GetMyCPUTime()-t1 << "; ";
	trialfile.close();

	solution = new PLA(implpool);
	if ( solution == NULL) throw RuntimeException( "Cover: Not enough memory" );
	for ( sol_it = sol->begin(); sol_it != sol->end(); sol_it++ )
		solution->AppendTerm( *implpool->getTermByIndex(*sol_it) );

	delete CM;
	delete sol;			// deallocates solution allocated by CP solver

//	TS.stop(TS_CP);
	return solution;
}

void Cover::Sparse_output( PLA *pla )
	// Removes unnecessary 1's from the output matrix
	// (by solving m separate covering problems)
	// Input of sparsed terms is expanded
	// Does not modify the number of terms
{
	CovMatrix *CM;
	unsigned int i, j, k;
	TermList::iterator ti;
	unsigned int rownum, colnum;
	list<unsigned int> *sol;
	list<unsigned int>::iterator sol_it;
	vector<bool> solvector;

	//	TS.start(TS_Sparse);
	for ( ti = pla->getData()->begin(); ti != pla->getData()->end(); ++ti )
		ti->tmp = 0;			// candidates for input expansion

	for ( i = 0; i < pla->outputs(); i++ ) {	// generate cover matrix for this output		
		// determine the number of rows by the first one
		ti = pla->getData()->begin();		// determine vector size by the first one
		if ( ti->getSO_CoverVector(i)->empty() ) GenerateCoverVectors(*pla);
		rownum = ti->getSO_CoverVector(i)->size();
		if ( rownum == 0 ) continue;		// trivial case
		// count the number of 1's in the OM[i]
		colnum = 0;
		for ( ti = pla->getData()->begin(); ti != pla->getData()->end(); ++ti )
			if ( ti->isOM_1(i) ) colnum++;
		if ( colnum == 0 ) continue;		// trivial case
		CM = new CovMatrix( rownum, colnum );
		if ( CM == NULL) throw RuntimeException( "Cover: Not enough memory" );
		k = 0;
		for ( ti = pla->getData()->begin(); ti != pla->getData()->end(); ++ti ) {
			if ( ti->getSO_CoverVector(i)->size() != rownum )
				throw RuntimeException( "Cover::Sparse_output:: Bad size of cover vector" );
			if ( ti->isOM_1(i) ) {
				for ( j = 0; j < rownum; j++ )
					CM->SetValue( j, k, (*(*ti).getSO_CoverVector(i))[j] == ZERO ? false : true);
				k++;
			}
		}
		// solve it
		sol = Solve(CM);

		// substitute unnecessary 1's by ~
		solvector.clear();
		solvector.resize( colnum, false );
		for ( sol_it = sol->begin(); sol_it != sol->end(); ++sol_it )
			solvector[*sol_it] = true;
		k = 0;
		for ( ti = pla->getData()->begin(); ti != pla->getData()->end(); ++ti )
			if ( ti->isOM_1(i) )
				if ( !solvector[k++] ) {
					ti->setOM_undef(i);
					ti->tmp = 1;
				}
				delete CM;
				delete sol;			// deallocates solution allocated by CP solver

	}
	// expand input for sparsed terms
	for ( ti = pla->getData()->begin(); ti != pla->getData()->end(); ++ti )
		if ( ti->tmp == 1 ) Expand(source).ExpandTerm(*ti);

	//	TS.stop(TS_Sparse);
}


list<unsigned int> *Cover::Solve( CovMatrix *CM )
{
	switch ( method ) {
		case LCMC: return HeurCover(CM).Run();
		case Aura: return AuraCover(CM).Run();
		case BBLag: return LagBaBCover(CM).Run();
		default: throw RuntimeException("Cover::Method not implemented yet");
	}
}

