// Asynch_DIMS.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"

using namespace std;

#include "../../Kernel/kernelincludes.h"
#include "../../Multilevel/Blif.h"
#include "../../Multilevel/Signal.h"
#include "../../Multilevel/Node.h"
#include "../../Multilevel/Network.h"
#include "../../Kernel/Complement.h"

unsigned int GateLevels = 0, NodeLevels = 0, MixedLevels = 0;
unsigned int nodes[8];


void Help()
{
	cout << "Syntax: Asynch_DIMS source.blif" << endl;
	cout << "Syntax: Asynch_DIMS -Nielsen source.blif" << endl;
	cout << "Syntax: Asynch_DIMS -dims2 source.blif" << endl;
	cout << "Syntax: Asynch_DIMS -NCL source.blif" << endl;
}

unsigned int GetCombinedLevels( Blif &source )
	// returns the number of levels of the Combinad implementation
{
	list<SoPLA>::iterator pla_it, pla_it2;
	bool change;
	unsigned int my_max = 1;
	unsigned int i, j;
	
	for ( pla_it = source.getData()->begin(); pla_it != source.getData()->end(); pla_it++ )
		(*pla_it).tmp = 0;
	for ( j = 0; j < source.inputs(); j++ )
		for ( pla_it = source.getData()->begin(); pla_it != source.getData()->end(); pla_it++ )
			for ( i = 0; i < (*pla_it).inputs(); i++ )
				if ( source.getIName(j) == (*pla_it).getIName(i) ) {
					(*pla_it).tmp = 1;
					if ( (*pla_it).tmp1 == 0 )
						if ( (*pla_it).getTerms() > 1 ) {
							(*pla_it).tmp++;
							my_max = 2;
						}
				}
	do {
		change = false;
		for ( pla_it = source.getData()->begin(); pla_it != source.getData()->end(); pla_it++ )
			if ( (*pla_it).tmp != 0 )
				for ( pla_it2 = source.getData()->begin(); pla_it2 != source.getData()->end(); pla_it2++ )
					if ( pla_it != pla_it2 && (*pla_it2).inputs() > 1 )
						for ( i = 0; i < (*pla_it2).inputs(); i++ )
							if ( (*pla_it).getName() == (*pla_it2).getIName(i) ) 
								if ( (*pla_it2).tmp <= (*pla_it).tmp ) {
									(*pla_it2).tmp = (*pla_it).tmp + 1;
									if ( (*pla_it2).tmp1 == 0 )		// as AND-OR DIMS
										if ( (*pla_it2).getTerms() > 1 ) (*pla_it2).tmp++;
									if ( (*pla_it2).tmp > my_max ) my_max = (*pla_it2).tmp;
									change = true;	
									break;
								}
	} while (change);
	return my_max;
}

unsigned int GetGate_DIMS( Blif &source )
// Returns 2*GEs for each AND and OR gate DIMS implementation
{
	unsigned int GEs = 0;
	list<SoPLA>::iterator PLA_it;
	list<Cube>::iterator ti;
	unsigned int K, N = 0;

	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); ++PLA_it ) {
		if ( (*PLA_it).inputs() == 1 ) continue;		// filter invertors and constants		
		for ( ti = (*PLA_it).getData()->begin(); ti != (*PLA_it).getData()->end(); ++ti ) {
			N = (*PLA_it).inputs()-(*ti).getDim();		// term literals
			GEs += (1 << N)*(N+1)*2;	// 2^N N-input C-elements for AND gate
			GEs += (1 << N)-1;			// (2^N-1)-input OR
		}
		K = (*PLA_it).getTerms();	// Node terms
		if ( K == 1 ) continue;		// Just one-AND node, no OR
		GateLevels++;
		GEs += (1 << K)*(N+1)*2;	// 2^K K-input C-elements for OR gate
		GEs += (1 << K)-1;			// (2^K)-1-input OR
	}

	return GEs;
}

unsigned int GetNode_DIMS( Blif &source )
// Returns 2*GEs for each node DIMS implementation
{
	unsigned int GEs = 0;
	list<SoPLA>::iterator PLA_it;
	unsigned int N;
	SoPLA *complement;

	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); PLA_it++ ) {
		if ( (*PLA_it).inputs() == 1 ) continue;		// filter invertors and constants
		N = (*PLA_it).inputs();	// # of node inputs
		GEs += (1 << N)*(N+1)*2;	// 2^N N-input C-elements for AND gate
		GEs += (1 << N)-1;			// (2^N)-1-input OR
		if ( (*PLA_it).getTerms() == 1 && (*PLA_it).getData()->front().getDim() == 0 ) continue;
		complement = Complement().PLAComplement(&(*PLA_it));
		if ( complement->getTerms() != 1 || complement->getData()->front().getDim() != 0 ) GEs++;	// complement has more than one terms, which indices 2^N-input OR
		delete complement;
	}
	return GEs;
}

unsigned int GetCombined( Blif &source )
// Returns 2*GEs for the combined implementation
{
	unsigned int GEs = 0;
	unsigned int node_GEs1, node_GEs2;
	list<SoPLA>::iterator PLA_it;
	list<Cube>::iterator ti;
	unsigned int K, N = 0;
	SoPLA *complement;

	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); PLA_it++ ) {
		if ( (*PLA_it).inputs() == 1 ) continue;		// filter invertors and constants
			// GEs for each AND and OR gate DIMS implementation
		node_GEs1 = 0;
		for ( ti = (*PLA_it).getData()->begin(); ti != (*PLA_it).getData()->end(); ti++ ) {
			N = (*PLA_it).inputs()-(*ti).getDim();		// term literals
			node_GEs1 += (1 << N)*(N+1)*2;	// 2^N N-input C-elements for AND gate
			node_GEs1 += (1 << N)-1;			// (2^N-1)-input OR
		}
		K = (*PLA_it).getTerms();	// Node terms
		if ( K != 1 ) {				// Just one-AND node, no OR
			node_GEs1 += (1 << K)*(N+1)*2;	// 2^K K-input C-elements for OR gate
			node_GEs1 += (1 << K)-1;			// (2^K-1)-input OR
		}
			// GEs for each node DIMS implementation
		node_GEs2 = 0;
		N = (*PLA_it).inputs();	// # of node inputs
		node_GEs2 += (1 << N)*(N+1)*2;	// 2^N N-input C-elements for AND gate
		node_GEs2 += (1 << N)-1;			// (2^N-1)-input OR
		if ( (*PLA_it).getTerms() != 1 && (*PLA_it).getData()->front().getDim() == 0 ) {
			complement = Complement().PLAComplement(&(*PLA_it));
			if ( complement->getTerms() != 1 || complement->getData()->front().getDim() != 0 ) node_GEs2++;	// complement has more than one terms, which indices 2^N-input OR
			delete complement;
		}

		if ( node_GEs1 < node_GEs2 ) {
			(*PLA_it).tmp1 = 0;			// this node is as AND-OR DIMS
			GEs += node_GEs1;
		}
		else {
			(*PLA_it).tmp1 = 1;			// this node is as node DIMS
			GEs += node_GEs2;
		}
	}

	return GEs;
}

unsigned int GetNielsenTransistors( Blif &source )
// Count the number of transistors of Nielsen DIMS
{
	list<SoPLA>::iterator PLA_it;
	unsigned int trans = 0;

	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); PLA_it++ )
		switch ( (*PLA_it).inputs() ) {
			case 2: trans += 22;
				break;
			case 3: trans += 34;
				break;
			case 4: trans += 54;
				break;
			case 5: trans += 90;
				break;
			case 6: trans += 158;
				break;
		}
	return trans;
}

unsigned int GetDims2Transistors( Blif &source )
// Count the number of transistors of Nielsen DIMS
{
	list<SoPLA>::iterator PLA_it;
	unsigned int trans = 0;

	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); PLA_it++ )
		switch ( (*PLA_it).inputs() ) {
			case 2: trans += 24;
				break;
			case 3: trans += 64;
				break;
			case 4: trans += 160;
				break;
			case 5: trans += 384;
				break;
			case 6: trans += 896;
				break;
		}
	return trans;
}

unsigned int GetNCLTransistors( Blif &source )
// Count the number of transistors of Nielsen DIMS
{
	list<SoPLA>::iterator PLA_it;
	unsigned int trans = 0;

	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); PLA_it++ ) {
		if ( (*PLA_it).inputs() > 2 ) throw RuntimeException( "GetNCLTransistors: unknown gate" );
		if ( (*PLA_it).inputs() > 1 ) {
			switch ( (*PLA_it).getTerms() ) {
				case 1: trans += 21;
					break;
				case 2: trans += 24;
					break;
				default: throw RuntimeException( "GetNCLTransistors: unknown gate" );
					break;
			}
		}
	}
	return trans;
}

void GetX_Nodes( Blif &source )
{
	list<SoPLA>::iterator PLA_it;
	unsigned int i;

	for ( i = 0; i < 8; i++ ) nodes[i] = 0;
	for ( PLA_it = source.getData()->begin(); PLA_it != source.getData()->end(); PLA_it++ )
		if ( (*PLA_it).inputs() < 7 ) nodes[(*PLA_it).inputs()]++;
		else nodes[7]++;
}

int main(int argc, char* argv[])
{
    Blif source;
	char *src_file;
	unsigned int Gate_DIMS_GEs = 0, Node_DIMS_GEs = 0, combined_GEs = 0, levels = 0, GEs = 0;
	unsigned int Gate_DIMS_Levels = 0, Node_DIMS_Levels = 0, combined_Levels = 0;
	unsigned int i = 1;
	unsigned int mode = 0; // Nielsen = 1, dims2 = 2
	unsigned int trans = 0;
	Network *net;
	float delay = 0;
	float mindelay = 0, avgdelay = 0;

	cout << "Asynch-DIMS 2.6-tmp (hardwired NCL delays), (c) February 2010 by Petr Fiser" << endl;
	if ( argc < 2 ) {
        Help();
        exit(1);
    }
	
	if ( strcmp(argv[1], "-Nielsen") == 0 ) {
		i++;
		mode = 1;
	}
	else if ( strcmp(argv[1], "-dims2") == 0 ) {
		i++;
		mode = 2;
	}

	else if ( strcmp(argv[1], "-NCL") == 0 ) {
		i++;
		mode = 3;
	}

	src_file = argv[i];
	if ( src_file == NULL ) {
        Help();
        exit(1);
    }	

	try {
		source.LoadBlif(src_file);
	} catch (IOException e) {
			cerr << e.GetMsg() << endl;
			exit(1);
	}

	levels = source.GetLevels();
	GEs = source.getGEs();

	if ( mode == 0 ) {
		Gate_DIMS_GEs = GetGate_DIMS(source);
		Node_DIMS_GEs = GetNode_DIMS(source);
		combined_GEs = GetCombined(source);
		Gate_DIMS_Levels = source.GetANDORLevels()*2;
		Node_DIMS_Levels = levels*2;
		combined_Levels = GetCombinedLevels(source)*2;
	} else if ( mode == 1 ) {
		trans = GetNielsenTransistors(source);
		GetX_Nodes(source);
		net = new Network(source);
		delay = net->GetDelay();
		mindelay = net->GetMinDelay();
		avgdelay = net->GetAvgDelay();
	} else if ( mode == 2 ) {
		trans = GetDims2Transistors(source);
		GetX_Nodes(source);
		net = new Network(source);
		delay = net->GetDelay();
		mindelay = net->GetMinDelay();
		avgdelay = net->GetAvgDelay();
	} else if ( mode == 3 ) {
		trans = GetNCLTransistors(source);
		GetX_Nodes(source);
		net = new Network(source);
		delay = net->GetNCLDelay();
		mindelay = net->GetMinDelay();
		avgdelay = net->GetAvgDelay();
	} else throw RuntimeException( "Unknown operation" );

	cout << "Nodes: " << source.GetNodes() << endl;
	cout << "Non-trivial nodes: " << source.GetNonTrivNodes() << endl;
	cout << "Source network levels: " << levels << endl;
	if ( mode == 0 ) {
		cout << "Synchronous: " << (float)GEs/2 << " GEs" << endl;
		cout << "DIMS for each gate: " << (float)Gate_DIMS_GEs/2 << " GEs" << endl;
		cout << "DIMS for each node: " << (float)Node_DIMS_GEs/2 << " GEs" << endl;
		cout << "Combined: " << (float)combined_GEs/2 << " GEs" << endl;
		cout << "DIMS for each gate: " << Gate_DIMS_Levels << " levels" << endl;
		cout << "DIMS for each node: " << Node_DIMS_Levels << " levels" << endl;
		cout << "Combined: " << combined_Levels << " levels" << endl;
	} else {
		cout << "Synchronous: " << GEs*2 << " transistors" << endl;
		cout << "Asynchronous: " << trans << " transistors" << endl;
		cout << "Max. delay: " << delay << endl;
		cout << "Min. delay: " << mindelay << endl;
		cout << "Avg. delay: " << avgdelay << endl;
		for ( i = 1; i < 7; i++ ) 
			cout << i << "-input nodes: " << nodes[i] << endl;
		cout << "more-input nodes: " << nodes[7] << endl;
	}
	cerr << src_file << "; " << source.GetNodes() << "; " << source.GetNonTrivNodes() << "; " << levels << "; ";
	if ( mode == 0 ) cout << GEs/2 << "; " << (float)Gate_DIMS_GEs/2 << "; " << (float)Node_DIMS_GEs/2 << "; " << (float)combined_GEs/2 << "; " << Gate_DIMS_Levels << "; " << Node_DIMS_Levels << "; " << combined_Levels << endl;
	else {
		cerr << GEs*2 << "; " << trans << "; " << delay << "; " << mindelay << "; " << avgdelay;
		for ( i = 1; i < 8; i++ ) 
			cerr << "; " << nodes[i];
		cerr << endl;
	}

	return 0;
}
