#include "stdafx.h"

#include <time.h>

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

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

unsigned int cnt = 0;

void Help()
{
	cout << "Syntax: BlowupNetwork [-inv n] [-dupl n] [-depth n] source.blif dest.blif" << endl;
	cout << "Options:" << endl;
	cout << "  -inv n      Number of added pairs of inverters. Default = 0" << endl;
	cout << "  -dupl n     Duplicate n branches. Default = 0" << endl;
	cout << "  -depth n    Depth of duplicate branches. Default = infinity" << endl;
}

void AddInverters( Network &net )
{
	unsigned int i;
	list<Node>::iterator ni;
	PLA *gate_pla;
	PLA *inv_pla;
	Term t;
	ostringstream ss;
	string num;

	i = rand() % net.GetNodes();

	ss << cnt;
	num = ss.str();
	
	ni = net.GetNodeByIndex(i);
	gate_pla = (*ni).getData();
	t.setCube("0");
	t.setOM("1");

	inv_pla = new PLA(1, 1, PLA_type("f"));
	inv_pla->setIName( 0, gate_pla->getOName(0) + "_" + num );
	inv_pla->setOName( 0, gate_pla->getOName(0) + "_i_" + num  );
	inv_pla->AppendTerm(t);
	net.AddNode(Node(*inv_pla));
	delete inv_pla;

	inv_pla = new PLA(1, 1, PLA_type("f"));
	inv_pla->setIName( 0, gate_pla->getOName(0)+ "_i_" + num );
	inv_pla->setOName( 0, gate_pla->getOName(0) );
	inv_pla->AppendTerm(t);
	net.AddNode(Node(*inv_pla));
	delete inv_pla;
	(*ni).getData()->setOName(0, gate_pla->getOName(0)+ "_" + num );

	cnt++;
}

string GetUniqueNameClone( Network &net, const string &name )
{
	string s;
	unsigned int i;
	ostringstream ss;

	i = 0;
	do {
		ss << i;
		s = name + "_" + ss.str();
	} while (net.FindSignal(s).first);
	return s;
}


void DuplicateBranch( Network &net, unsigned int depth )
// Only single-output nodes are supported
{
	list<Signal> *signals;
	list<Signal>::iterator sigit;
	vector<list<Signal>::iterator> candidates;
	unsigned int pos;
	string name, newname;
	unsigned int i;
	list<Node>::iterator ndi;
	list<pair<list<Node>::iterator, unsigned int> >::iterator destit;
	queue<pair<list<Node>::iterator, unsigned int> > nodequeue;
	Node *newnode;
	map<string, string> signalmap;
	pair<bool, list<Signal>::iterator> findres;

	// select one random branch
	signals = net.GetSignals();
	for ( sigit = signals->begin(); sigit != signals->end(); sigit++ )
		if ( (*sigit).GetDest()->size() > 1 && !(*sigit).IsPI() ) candidates.push_back(sigit);
	if ( candidates.size() == 0 ) return;
	sigit = candidates[rand() % candidates.size()];
	
	// select ranom branch destination node
	pos = rand() % (*sigit).GetDest()->size();
	destit = (*sigit).GetDest()->begin();
	for ( i = 0; i < pos; i++ ) destit++;

	// generate new net name
	name = GetUniqueNameClone( net, (*sigit).getName() );
	signalmap.insert( make_pair((*sigit).getName(), name));
	
	// Reconnect gate input
	net.ReconnectInput( (*destit).first,  (*destit).second, name );

	// Put source node into a queue
	nodequeue.push(make_pair((*sigit).GetSource()->first, 0));

	while ( !nodequeue.empty() ) {
		ndi = nodequeue.front().first;
		if ( (*ndi).outputs() != 1 ) throw RuntimeException( "DuplicateBranch: Multipleoutput nodes not supported" );
		newnode = new Node(*ndi);
			// rename output
		name = newnode->getData()->getOName(0);
		newname = (*signalmap.find(name)).second;
		newnode->getData()->setOName(0, newname);
			// rename inputs & enqueue
		if ( nodequeue.front().second < depth || depth == 0 ) {
			for ( i = 0; i < newnode->inputs(); i++ )
				if ( !net.isPI(newnode->getData()->getIName(i) )) {
						// rename
					name = newnode->getData()->getIName(i);
					if ( signalmap.find(name) != signalmap.end() ) {		// signal already exists - reconvergence
						newname = (*signalmap.find(name)).second;
						newnode->getData()->setIName(i, newname);
					}
					else {		// signal not found
						newname = GetUniqueNameClone( net, name );
						signalmap.insert( make_pair(name, newname) );
						newnode->getData()->setIName(i, newname);
							// enqueue
						findres = net.FindSignal(name);
						if ( !findres.first ) throw RuntimeException( "DuplicateBranch: Signal not found" );
						nodequeue.push( make_pair((*findres.second).GetSource()->first, nodequeue.front().second+1 ));
					}	
				}
		}
		net.AddNode(*newnode);
		delete newnode;
		nodequeue.pop();
	}
}


int main(int argc, char* argv[])
{
    Blif source;
    Blif *dest;
    int i;
	char *src_file = NULL;
	char *dest_file = NULL;
	Network *net;
	unsigned int inv = 0;
	unsigned int dupl = 0;
	unsigned int depth = 0;

	cout << "BlowupNetwork 2.1, (c) February 2010 by Petr Fiser" << endl;
	if ( argc < 4 ) {
        Help();
        exit(1);
    }
//    srand((unsigned) time(&tim));
    srand(3);

	try {
		i = 1;
		while ( i != argc ) {
			if ( argv[i][0] != '-' && src_file == NULL ) src_file = argv[i];
			else if ( argv[i][0] != '-' ) dest_file = argv[i];

			else if ( strcmp( argv[i], "-inv" ) == 0 ) {
				i++;
				inv =  atoi(argv[i]);
			}
			else if ( strcmp( argv[i], "-dupl" ) == 0 ) {
				i++;
				dupl =  atoi(argv[i]);
			}
			else if ( strcmp( argv[i], "-depth" ) == 0 ) {
				i++;
				depth =  atoi(argv[i]);
			}
			else {
				Help();
				exit(1);
			}
			i++;
		}
	} catch (...) {
        Help();
        exit(1);
    }

	if ( src_file == NULL || dest_file == NULL ) {
        Help();
        exit(1);
    }

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

	net = new Network(source);

	while ( dupl > 0 ) {
		DuplicateBranch(*net, depth);
		dupl--;
	}

	while ( inv > 0 ) {
		AddInverters(*net);
		inv--;
	}

	dest = net->ToBlif();
	dest->SaveBlif(dest_file);
	delete dest;
	delete net;

	return 0;
}
