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

#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 )
{
    implpool = _implpool;
    source = _source;
    method = DEFAULT_IE;
}

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
{
    unsigned int ov;
	unsigned int i, i_start;
    TermList::iterator ti;
    TermList newterms;          // newly created terms
    Term t;                     // term under construction
    TermList::iterator ti1;     // newly created term
    bool change;                // is there any change?
    char c;

	for ( ov = 0; ov < implpool->GetOutputs(); ov++ ) {
		for ( ti = implpool->GetData()->begin(); ti != implpool->GetData()->end(); ti++ )
		  if ( (*ti).GetOM(ov) == '1' ) {
			t = *ti;
			change = false;
			i_start = rand() % implpool->GetInputs();
			i = i_start;
			do {
				if ( t.GetIM(i) != '-' ) {
					c = t.GetIM(i);
					t.SetIM( i, '-' );
					if ( !source->IsImplicant( t, ov ) ) t.SetIM( i, c );
					else change = true;
				}
				i++;
				i %= implpool->GetInputs();
			} while ( i != i_start );
			if ( change ) {
				for ( i = 0; i < t.GetOMSize(); i++ )
					if ( i != ov && t.GetOM(i) == '1' )
						if ( !source->IsImplicant( t, i ) ) t.SetOM(i, '0');
				t.origin |= 4;
				newterms.push_back(t);
			}
		}
		for ( ti = newterms.begin(); ti != newterms.end(); ti++ ) {
			ti1 = implpool->AppendTerm(*ti);
			for ( i = 0; i < (*ti).GetOMSize(); i++ )                    // for a case the term is already present in TT, but having different OM
				if ( (*ti1).GetOM(i) == '~' && (*ti).GetOM(i) != '~' )
					(*ti1).SetOM(i, (*ti).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
{
    unsigned int ov;
	unsigned int i;
    TermList::iterator ti;
    Term t;                     // term under construction
    TermList::iterator ti1;                   // newly created term
    char c;

	for ( ov = 0; ov < implpool->GetOutputs(); ov++ ) {
		for ( ti = implpool->GetData()->begin(); ti != implpool->GetData()->end(); ti++ )
		  if ( (*ti).GetOM(ov) == '1' ) {
			t = *ti;
			for ( i = 0; i < implpool->GetInputs(); i++ )
				if ( t.GetIM(i) != '-' ) {
					c = t.GetIM(i);
					t.SetIM( i, '-' );
					if ( !source->IsImplicant( t, ov ) ) t.SetIM( i, c );
					else {
						for ( i = 0; i < t.GetOMSize(); i++ )
							if ( i != ov && t.GetOM(i) == '1' )
								if ( !source->IsImplicant( t, i ) ) t.SetOM(i, '0');
						t.origin |= 4;
						ti1 = implpool->AppendTerm(t);
						for ( i = 0; i < t.GetOMSize(); i++ )                    // for a case the term is already present in TT, but having different OM
							if ( (*ti1).GetOM(i) == '~' && (*ti).GetOM(i) != '~' )
								(*ti1).SetOM(i, t.GetOM(i));
					}
				}
		}
	}
}

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

	for ( ov = 0; ov < implpool->GetOutputs(); ov++ ) {
		for ( ti = implpool->GetData()->begin(); ti != implpool->GetData()->end(); ti++ )
		  if ( (*ti).GetOM(ov) == '1' ) {
			t = *ti;
			for ( i = 0; i < implpool->GetInputs(); i++ )
				if ( t.GetIM(i) != '-' ) {
					c = t.GetIM(i);
					t.SetIM( i, '-' );
					if ( !source->IsImplicant( t, ov ) ) t.SetIM( i, c );
					else {
						for ( j = 0; j < t.GetOMSize(); j++ )
							if ( j != ov && t.GetOM(j) == '1' )
								if ( !source->IsImplicant( t, j ) ) t.SetOM(j, '0');
						t.origin |= 4;
						ti1 = implpool->AppendTerm(t);
						for ( j = 0; j < t.GetOMSize(); j++ )                    // for a case the term is already present in TT, but having different OM
							if ( (*ti1).GetOM(j) == '~' && (*ti).GetOM(j) != '~' )
								(*ti1).SetOM(j, t.GetOM(j));
						t.SetIM( 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;			// already tried for removal
	unsigned int trials;		// countdown

	tried.assign( implpool->GetTerms() * implpool->GetInputs(), false );
	trials = implpool->GetTerms() * implpool->GetInputs();

	do {
		pos = rand() % (implpool->GetTerms() * implpool->GetInputs());
		do {
			if ( !tried[pos] ) {
				ti = implpool->GetTermByIndex( pos / implpool->GetInputs() );
				termpos = pos % implpool->GetInputs();
				tried[pos] = true;
				trials--;
				if ( (*ti).GetIM(termpos) != '-' ) {
					c = (*ti).GetIM(termpos);
					(*ti).SetIM(termpos, '-');
					if ( !source->IsImplicant(*ti) ) (*ti).SetIM(termpos, c);
					else (*ti).origin |= 4;
				}
				break;
			} else pos = (pos+1) % (implpool->GetTerms() * implpool->GetInputs());
		} while ( trials > 0 );
	} while ( trials > 0 );
}

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->GetInputs();
	i = start;
	do {
		c = t.GetIM(i);
		if ( c != '-' ) {	
			t.SetIM(i, '-');
			for ( ov = 0; ov < source->GetOutputs(); ov++ )
				if ( t.GetOM(ov) == '1' )
					if ( !source->IsImplicant( t, ov ) ) break;
			if ( ov != source->GetOutputs() ) t.SetIM(i, c);
		}
		i++;
		i %= source->GetInputs();
	} 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);
}
