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

#include "stdafx.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

//#include <fstream>

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

#include "CD_search.h"
#include "FC_Min.h"
#include "Expand.h"
#include "Cover.h"
#include "Reduce.h"

#include "defaults.h"
//#include "TimeStamp.h"
#include "BoomAlg.h"

#include "parallel.h"
//---------------------------------------------------------------------------

template<class T, void(T::*member_function)()>		// template wrapper for passing pointers to class member functions
void * thread_wrapper(void * p) {
	(reinterpret_cast<T *>(p)->*member_function)();
	return NULL;
}

struct ras {
	PLA * pla;
	TermQueue * tq_in;
	TermQueue * tq_rabc;
};

void * RemAbsb(void * s) {
	ras * r = (ras *) s;
	r->pla->RemoveAbsorbed(r->tq_in, r->tq_rabc);
	return NULL;
}

BoomOpts::BoomOpts(void)
{
	CD_FC = DEFAULT_CD_FC;
	iterations = DEFAULT_iterations;
	CD_mutations = DEFAULT_CD_MUTATIONS;
	CD_implcheck = DEFAULT_CD_IMPLCHECK;
	IE = DEFAULT_IE;
	IR = DEFAULT_IR;
	DF = DEFAULT_DF;
	CP = DEFAULT_CP;
}

BoomAlg::BoomAlg(void)
{
	implpool = NULL;
	FC_pool = NULL;
	result = NULL;
}

BoomAlg::BoomAlg( PLA *_pla )
{
    source = _pla;
    result = NULL;
	implpool = new PLA( source->inputs(), source->outputs(), PLA_type("f") );
	if ( implpool == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
    //implpool->setUnique();
	//implpool->keepTT();		// slows it down (?)
	FC_pool = new PLA( source->inputs(), source->outputs(), PLA_type("f") );
	if ( FC_pool == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
//    FC_pool->setUnique();
//	FC_pool->keepTT();
}

BoomAlg::~BoomAlg(void)
{
	delete implpool;
}

PLA *BoomAlg::Run(void)
{
    CD_Search *CDS_alg = NULL;
	FC_Min *FC_alg = NULL;
	Expand *IE_alg = NULL;
	Expand *IE_FC_alg = NULL;
	Reduce *IR_alg = NULL;
	Cover *CP_alg = NULL;
	PLA *sol;

	pthread_t cdt, fcmt, fcmiet, iet, gomt, rat, irt, rabct;
	TermQueue tq_cds_ie, tq_fcm_fcie, tq_ie_gom, tq_gom_ra, tq_ir_rabc;

	// initialize algorithms
	if ( options.CD_FC != 100 ) {
		CDS_alg = new CD_Search( source, implpool, (options.iterations * (100 - options.CD_FC)) / 100, &tq_cds_ie, &tq_ie_gom, options.IE != no_IE );
		if ( CDS_alg == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
	}
	if ( options.CD_FC != 0 ) {
		FC_alg = new FC_Min( source, FC_pool, (options.iterations * options.CD_FC) / 100, &tq_fcm_fcie);
		if ( FC_alg == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
		FC_alg->SetDF(options.DF);
		IE_FC_alg = new Expand( source, FC_pool, &tq_fcm_fcie, &tq_ie_gom, &tq_cds_ie );
		if ( IE_FC_alg == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
		IE_FC_alg->SetMethod(scatter_IE);
	}
	if ( options.IE != no_IE ) {
		IE_alg = new Expand( source, implpool, &tq_cds_ie, &tq_ie_gom );
		if ( IE_alg == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
		IE_alg->SetMethod(options.IE);
	}

	CP_alg = new Cover( source, implpool, &tq_ie_gom, &tq_gom_ra, &tq_ir_rabc);
	CP_alg->SetMethod(options.CP);
	if ( CP_alg == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
	if ( options.IR != no_IR ) {
		IR_alg = new Reduce( source, implpool, &tq_ir_rabc, CP_alg);
		if ( IR_alg == NULL) throw RuntimeException( "BoomAlg: Not enough memory" );
		IR_alg->SetMethod(options.IR);
	}

	// remove clearly absorbed terms
	source->RemoveAbsorbed();

	// Run CD-S and FC-Min
	if ( options.CD_FC != 100 ) {
		pthread_create(&cdt, NULL, thread_wrapper<CD_Search, &CD_Search::Run>, CDS_alg);
		//cout << "CD-S " << (options.iterations * (100 - options.CD_FC)) / 100 << endl;
	}
	if ( options.CD_FC != 0 ) {
		pthread_create(&fcmt, NULL, thread_wrapper<FC_Min, &FC_Min::Run>, FC_alg);
		pthread_create(&fcmiet, NULL, thread_wrapper<Expand, &Expand::Run>, IE_FC_alg);
	}
	//}

	//implpool->SavePLA("implpool.pla");
	
	// Reductions, Expansions, CP
	if (options.IE != no_IE)
		pthread_create(&iet, NULL, thread_wrapper<Expand, &Expand::Run>, IE_alg);

	pthread_create(&gomt, NULL, thread_wrapper<Cover, &Cover::GenerateOM>, CP_alg);

	ras r;
	r.pla = implpool;
	r.tq_in = &tq_gom_ra;
	r.tq_rabc = &tq_ir_rabc;

	pthread_create(&rat, NULL, RemAbsb, &r);

	if ( options.CD_FC != 100 )
		pthread_join(cdt, NULL);

	Term t;
	t.invalidate();

	if ( options.CD_FC != 0 ) {
		pthread_join(fcmt, NULL);
		tq_fcm_fcie.push(t);
		pthread_join(fcmiet, NULL);
	}

	if (options.IE != no_IE) {
		tq_cds_ie.push(t);
		pthread_join(iet, NULL);
	}

	tq_ie_gom.push(t);
	pthread_join(gomt, NULL);

	tq_gom_ra.push(t);
	pthread_join(rat, NULL);

	//if ( options.IR != no_IR )
	pthread_create(&irt, NULL, thread_wrapper<Reduce, &Reduce::Run>, IR_alg);

	pthread_create(&rabct, NULL, thread_wrapper<Cover, &Cover::RemoveAbsorbedByCover>, CP_alg);
	
	//if ( options.IR != no_IR )
	pthread_join(irt, NULL);

	tq_ir_rabc.push(t);
	pthread_join(rabct, NULL);
	
	sol = CP_alg->Run();

	CP_alg->Sparse_output(sol);

//	TS.DumpTimeStamps("timestamps.txt");

//	sol->DumpOrigin( "sol_origin.csv" );

	delete CP_alg;
	delete IR_alg;
	delete IE_alg;
	delete IE_FC_alg;
	delete FC_alg;
	delete CDS_alg;
	return sol;
}

/*PLA *BoomAlg::FC_Run(void)
{
	FC_Min *FC_alg = NULL;
	Expand *IE_FC_alg = NULL;
	Cover *CP_alg = NULL;
	unsigned int it, DF;
	PLA *sol;
	ofstream report;
	float t1, t2, t1_tot, t2_tot;

	report.open("FC-DF.csv");

	for ( DF = 0; DF < 100; DF++ ) {
			// initialize algorithms
		FC_pool->clear();
		implpool->clear();
		delete result;
		result = NULL;
		FC_alg = new FC_Min( source, FC_pool, 1, NULL );
		FC_alg->SetDF(DF);
		IE_FC_alg = new Expand( source, FC_pool, NULL, NULL );
		IE_FC_alg->SetMethod(scatter_IE);
		CP_alg = new Cover( source, implpool, NULL, NULL, NULL );

		t1 = GetMyCPUTime();
		t1_tot = t1;
		for ( it = 0; it < options.iterations; it++ ) {
			FC_alg->Run();
		}
		t2 = GetMyCPUTime();

		IE_FC_alg->Run();
		implpool->Splice( *FC_pool );

		CP_alg->GenerateOM();
		implpool->RemoveAbsorbed();
		CP_alg->RemoveAbsorbedByCover();

		sol = CP_alg->Run();
		CP_alg->Sparse_output(sol);
		
		t2_tot = GetMyCPUTime();

		report << DF << "; " << FC_alg->GetTrials() << "; " << FC_alg->GetHits() << "; " << sol->getLiterals() << "; " << sol->getTerms() << "; " << t2-t1 << "; " << t2_tot-t1_tot << endl;

		delete CP_alg;
		delete IE_FC_alg;
		delete FC_alg;
	}
	report.close();
    
	return sol;
}*/
