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

#include "stdafx.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

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

#include "defaults.h"
//#include "TimeStamp.h"
#include "Expand.h"
//---------------------------------------------------------------------------

Expand::Expand( PLA *_source, PLA *_implpool, TermQueue * tq_in, TermQueue * tq_out, TermQueue * tq_out2 )
{
    implpool = _implpool;
    source = _source;
    method = DEFAULT_IE;
	this->tq_in = tq_in;
	this->tq_out = tq_out;
	this->tq_out2 = tq_out2;
}

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

void Expand::FastExpand(void)
//	Starting from a random position removes literals, while the term is an implicant of ov.
// All output variables are handled separately, thus the number of implicants may be increased.
// Keeps unexpanded implicants in the pool
{
    int ov;
	unsigned int i, i_start;
    //TermList::iterator t1;
    //TermList newterms;          // newly created terms
    Term t;                     // term under construction
    //TermList::iterator ti1;     // newly created term
    bool change;                // is there any change?
    char c;
	int inputs, outputs;

	inputs = implpool->inputs();
	outputs = implpool->outputs();
	while (true) {
		tq_in->pop(t);
		if (t.invalid()) break;
		#pragma omp parallel for default(none) \
			shared(outputs, inputs) private(ov, c, i, i_start, change) firstprivate(t)
		for ( ov = 0; ov < outputs; ov++ )
			if ( t.isOM_1(ov) ) {
				//t = *t1;
				change = false;
				i_start = rand() % inputs;
				i = i_start;
				do {
					if ( t.isLit_dc(i) ) {
						c = t.getLit(i);
						t.setLit_dc(i);
						if ( !source->isImplicant( t, ov ) ) t.setLit( i, c );
						else change = true;
					}
					i++;
					i %= inputs;
				} while ( i != i_start );
				if ( change ) {
					for ( i = 0; i < t.getOMSize(); i++ )
						if ( i != ov && t.isOM_1(i) )
							if ( !source->isImplicant( t, i ) ) t.setOM_0(i);
					t.origin |= 4;
					//newterms.push_back(t);
					tq_out->push(t);
				}
			}
			/*for ( t1 = newterms.begin(); t1 != newterms.end(); t1++ ) {
				ti1 = implpool->AppendTerm(*t1);
				for ( i = 0; i < (*t1).getOMSize(); i++ )                    // for a case the term is already present in TT, but having different OM
					if ( (*ti1).isOM_undef(i) && !(*t1).isOM_undef(i) )
						(*ti1).setOM(i, (*t1).getOM(i));
			}*/
	}
}

void Expand::MultipleExpand(void)
// Removes literals while the term is implicant of ov. Starts independently from all positions.
// Keeps unexpanded implicants in the pool
{
    int ov;
//    TermList::iterator ti;
    Term t;                     // term under construction
//    TermList::iterator ti1;                   // newly created term
	int inputs, outputs;		// local copy
//	list<Term> *data;					// local copy
	char c;

	inputs = (int) implpool->inputs();
	outputs = (int) implpool->outputs();
	while (true) {
		tq_in->pop(t);
		//t1 = t;
		if (t.invalid()) break;
		#pragma omp parallel for default(none) \
			shared(outputs, inputs) private(ov, c) firstprivate(t)
		for ( ov = 0; ov < outputs; ov++ ) {
			if ( t.isOM_1(ov) ) {
				//t = *ti;
				for (int i = 0; i < inputs; i++ )
					if ( !t.isLit_dc(i) ) {
						c = t.getLit(i);
						t.setLit_dc(i);
						if ( !source->isImplicant( t, ov ) ) t.setLit( i, c );
						else {
							for ( i = 0; i < (int) t.getOMSize(); i++ )
								if ( i != ov && t.isOM_1(i) )
									if ( !source->isImplicant( t, i ) ) t.setOM_0(i);
							t.origin |= 4;

							/*for ( i = 0; i < t.getOMSize(); i++ )                    // for a case the term is already present in TT, but having different OM
								if ( t.isOM_undef(i) && !t1.isOM_undef(i) )
									t.setOM(i, t.getOM(i));*/
							//ti1 = implpool->AppendTerm(t);
							tq_out->push(t);
						}
					}
			}
		}
	}
}

void Expand::ExhaustiveExpand(void)
//	Generates all prime implicants of ov
//  Keeps unexpanded and partially expanded implicants in the pool
{
    int ov;
    //TermList::iterator ti;
    Term t;                     // term under construction
    //TermList::iterator ti1;     // newly created term
    char c;
	int inputs, outputs;

	inputs = (int) implpool->inputs();
	outputs = (int) implpool->outputs();

	while (true) {
		tq_in->pop(t);
		if (t.invalid()) break;
		#pragma omp parallel for default(none) \
			shared(outputs, inputs) private(ov, c) firstprivate(t)
		for ( ov = 0; ov < outputs; ov++ )
			if ( t.isOM_1(ov) ) {
				//t = *ti;
				for (int i = 0; i < inputs; i++ )
					if ( !t.isLit_dc(i) ) {
						c = t.getLit(i);
						t.setLit_dc(i);
						if ( !source->isImplicant( t, ov ) ) t.setLit( i, c );
						else {
							for (int j = 0; j < (int) t.getOMSize(); j++ )
								if ( j != ov && t.isOM_1(j) )
									if ( !source->isImplicant( t, j ) ) t.setOM_0(j);
							t.origin |= 4;
							//ti1 = implpool->AppendTerm(t);
							tq_out->push(t);
							/*for ( j = 0; j < t.getOMSize(); j++ )                    // for a case the term is already present in TT, but having different OM
							if ( (*ti1).isOM_undef(j) && !(*ti).isOM_undef(j) )
							(*ti1).setOM(j, t.getOM(j));*/
							t.setLit( i, c );
						}
					}
			}
	}
}

void Expand::ScatterExpand(void)
// Randomly removes literals from term, so that no 1 in OM is harmed. Does not change the number of terms or their location
// PLA must not be set "unique"!!! It changes terms directly.
{
	//unsigned int pos;			// random term & position
	//unsigned int termpos;		// random position in the term
    //TermList::iterator ti;		// processed term
	char c;					// removed literal
	vector<bool> tried(implpool->inputs(), false);			// already tried for removal
	unsigned int trials = implpool->inputs(), i;		// countdown
	Term t;
	//unsigned int inputs = implpool->inputs();

	while (true) {
		tq_in->pop(t);
		if (t.invalid()) break;
		do {
			i = rand() % implpool->inputs();
			if (!tried[i]) {
				tried[i] = true;
				trials--;
				if ( !t.isLit_dc(i) ) {
					c = t.getLit(i);
					t.setLit_dc(i);
					if ( !source->isImplicant(t) ) t.setLit(i, c);
					else t.origin |= 4;
				}
			}
		} while (trials);
		tq_out->push(t);
		if (tq_out2)
			tq_out2->push(t);
		trials = implpool->inputs();
		tried.assign(implpool->inputs(), false);
	}
}

void Expand::ExpandTerm( Term &t )
// Tries to expand a single term, so that the output matrix will be kept
// Starts from a random position, like "fast"
{
	unsigned int start, i, ov;
	char c;

	start = rand() % source->inputs();
	i = start;
	do {
		c = t.getLit(i);
		if ( c != '-' ) {	
			t.setLit_dc(i);
			for ( ov = 0; ov < source->outputs(); ov++ )
				if ( t.isOM_1(ov) )
					if ( !source->isImplicant( t, ov ) ) break;
			if ( ov != source->outputs() ) t.setLit(i, c);
		}
		i++;
		i %= source->inputs();
	} while ( i != start );
}

void Expand::Run(void)
{
 //   TS.start(TS_IE);

	if ( implpool == NULL ) return;
    switch ( method ) {
		case no_IE:
			break;
        case fast_IE:
            FastExpand();
            break;
        case multiple_IE:
            MultipleExpand();
            break;
        case exhaustive_IE:
            ExhaustiveExpand();
            break;
        case scatter_IE:
            ScatterExpand();
            break;
    }
//	TS.stop(TS_IE);
}
