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

#include "stdafx.h"

using namespace std;

#ifdef _MSC_VER
#pragma hdrstop
#endif

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

#include "Blif.h"
#include "Node.h"
#include "Signal.h"
#include "Network.h"

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

Network::Network()
{
}

Network::Network( PLA &src )
// Construct the network from PLA. Creates a single node
{
	list<SoPLA>::iterator pla_it;

	nodes.push_back( src );
	inames = src.getINames();
	onames = src.getONames();
	validlevels = false;
//	Connect();
}

Network::Network( Blif &src )
// Construct the network from BLIF
{
	list<SoPLA>::iterator pla_it;

	for ( pla_it = src.getData()->begin(); pla_it != src.getData()->end(); ++pla_it ) 
		nodes.push_back( Node(*pla_it) );
	inames = src.getINames();
	onames = src.getONames();
	model = src.GetModel();
	validlevels = false;
//	Connect();
}

Network::Network( Network *net )
// Create an empty network with the source network port names
{
	inames = net->getINames();
	onames = net->getONames();
	validlevels = false;
}

Network::~Network()
{
}

list<Node>::iterator Network::AddNode( const Node &_node )
{
	list<Node>::iterator ni;

	nodes.push_back(_node);
	nodes.back().index = nodes.size()-1;
	ni = nodes.end();
	ni--;
	if ( !indexnodes.empty() ) indexnodes.push_back(ni);
	if ( !signals.empty() ) AddNodeSignals(ni);
	validlevels = false;
	return ni;
}

list<Node>::iterator Network::DeleteNode( const list<Node>::iterator ni )
{
	list<Node>::iterator tmp;
	unsigned int i;
	list<Signal>::iterator sigit;
	list<pair<list<Node>::iterator, unsigned int> >::iterator destit;
	pair<bool, list<Signal>::iterator> findres;

	if ( !signals.empty() ) {
		for ( i = 0; i < (*ni).outputs(); i++ )
			if ( !DeleteSignal( (*ni).getData()->getOName(i) )) throw RuntimeException( "Network::DeleteNode: Inconsistency between signals" );
		for ( i = 0; i < (*ni).inputs(); i++ ) {
			findres = FindSignal( (*ni).getData()->getIName(i) );
			if ( findres.first ) {		// signal need not be found, if it is a dangling node
				sigit = findres.second;
				destit = (*sigit).dest.begin();
				while ( destit != (*sigit).dest.end() ) {
					if ( (*destit).first == ni ) destit = (*sigit).dest.erase(destit);
					else destit++;
				}
			}
		}
	}

	validlevels = false;
	indexnodes.clear();
	tmp = nodes.erase(ni);
	return tmp;
}

void Network::Disconnect(void)
// completely clears the signals structure
{
	signals.clear();
	signalnames.clear();
}

Blif *Network::ToBlif(void) const
// Converts the network to Blif
// type specifies the Blif type (false = r, true = f)
{
	list<Node>::const_iterator nod_it;
	unsigned int i;
	Blif *dest;
	SoPLA *pla;

	dest = new Blif( inames, onames );
	dest->SetModel( model );
	for ( nod_it = nodes.begin(); nod_it != nodes.end(); nod_it++ )
		for ( i = 0; i < (*nod_it).data.outputs(); i++ ) {
			if ( (*nod_it).data.getType().f ) pla = (*nod_it).data.ToSoPLA( i, true );
			else pla = (*nod_it).data.ToSoPLA( i, false );
			pla->RemoveAllDCCols();
			dest->AppendPLA( pla );
			delete pla;
		}
	return dest;
}

Blif *Network::ToBlif( const bool type ) const
// Converts the network to Blif
// type specifies the Blif type (false = r, true = f)
{
	list<Node>::const_iterator nod_it;
	unsigned int i;
	Blif *dest;
	SoPLA *pla;

	dest = new Blif( inames, onames );
	for ( nod_it = nodes.begin(); nod_it != nodes.end(); nod_it++ )
		for ( i = 0; i < (*nod_it).data.outputs(); i++ ) {
			pla = (*nod_it).data.ToSoPLA( i, type );
			pla->RemoveAllDCCols();
			dest->AppendPLA( pla );
		}
	return dest;
}

void Network::ToSingleOutput(void)
// Converts the network into a network of single-output nodes, like Blif
{
	list<Node>::iterator nod_it, endnode;
	unsigned int i;
	SoPLA *pla;
	bool first = true;

	Disconnect();
	indexnodes.clear();
	nod_it = nodes.begin();
	if ( nod_it == nodes.end() ) return;
	validlevels = false;
	endnode = nodes.end();
	endnode--;
	do {
		if ( (*nod_it).data.outputs() == 1 ) nod_it++;
		else {
			for ( i = 0; i < (*nod_it).data.outputs(); i++ ) {
				if ( (*nod_it).data.getType().f ) pla = (*nod_it).data.ToSoPLA( i, true );
				else pla = (*nod_it).data.ToSoPLA( i, false );
				pla->RemoveAllDCCols();
				this->AddNode( Node(*pla) );
				delete pla;
				if (first) {
					endnode = nodes.end();
					endnode--;
					first = false;
				}
			}
			nod_it = nodes.erase(nod_it);
		}
	} while ( nod_it != endnode );
}

list<Node>::iterator Network::MergeNodes( list<Node>::iterator node1, list<Node>::iterator node2 )
// merges two nodes into one
{
	unsigned int i, j;
	PLA res_pla;
	vector<string> inames;
	vector<string> onames;
	vector<string>::iterator name_it;
	string im, om;
	Term t;
	TermList::iterator ti;
	list<Node>::iterator res;

	if ( (*node1).data.getType() != (*node1).data.getType() ) throw RuntimeException( "Network::MergeNodes: Merging incompatible nodes" );
	res_pla.setType( (*node1).data.getType() );
	Disconnect();
	indexnodes.clear();
	// Determine inputs
	for ( i = 0; i < (*node1).data.inputs(); i++ ) 
		inames.push_back( (*node1).data.getIName(i) );
	for ( i = 0; i < (*node2).data.inputs(); i++ ) {
		for ( name_it = inames.begin(); name_it != inames.end(); name_it++ )
			if ( (*node2).data.getIName(i) == (*name_it) ) break;
		if ( name_it == inames.end() ) inames.push_back( (*node2).data.getIName(i) );
	}
	res_pla.setInputs(inames.size());
	for ( i = 0; i < inames.size(); i++ ) res_pla.setIName( i, inames[i] );
	// Determine outputs
	for ( i = 0; i < (*node1).data.outputs(); i++ )
		onames.push_back( (*node1).data.getOName(i) );
	for ( i = 0; i < (*node2).data.outputs(); i++ ) {
		for ( name_it = onames.begin(); name_it != onames.end(); name_it++ )
			if ( (*node2).data.getOName(i) == (*name_it) ) break;
		if ( name_it == onames.end() ) onames.push_back( (*node2).data.getOName(i) );
	}
	res_pla.setOutputs(onames.size());
	for ( i = 0; i < onames.size(); i++ ) res_pla.setOName( i, onames[i] );
	// Construct PLA input matrix
	for ( ti = (*node1).data.getData()->begin(); ti != (*node1).data.getData()->end(); ++ti ) {
		t.init( inames.size(), onames.size() );
		for ( i = 0; i < (*ti).inputs(); i++ ) {
			for ( j = 0; j < t.inputs(); j++ )
				if ( inames[j] == (*node1).data.getIName(i) ) break;
			t.setLit( j, ti->getLit(i) );
		}
		for ( i = 0; i < (*ti).outputs(); i++ ) {
			for ( j = 0; j < t.outputs(); j++ )
				if ( onames[j] == (*node1).data.getOName(i) ) break;
			// if ( t.getOM(j) != '~' ) throw RuntimeException( "Network::MergeNodes: Multiple output drivers" );
			t.setOM( j, (*ti).getOM(i) );
		}
		res_pla.AppendTerm(t);
	}
	for ( ti = (*node2).data.getData()->begin(); ti != (*node2).data.getData()->end(); ++ti ) {
		t.init( inames.size(), onames.size() );
		for ( i = 0; i < ti->inputs(); i++ ) {
			for ( j = 0; j < t.inputs(); j++ )
				if ( inames[j] == (*node2).data.getIName(i) ) break;
			t.setLit( j, (*ti).getLit(i) );
		}
		for ( i = 0; i < (*ti).outputs(); i++ ) {
			for ( j = 0; j < t.outputs(); j++ )
				if ( onames[j] == (*node2).data.getOName(i) ) break;
			// if ( t.getOM(j) != '~' ) throw RuntimeException( "Network::MergeNodes: Multiple output drivers" );
			t.setOM( j, (*ti).getOM(i) );
		}
		res_pla.AppendTerm(t);
	}
	this->AddNode( Node(res_pla) );
	this->DeleteNode( node1 );
	this->DeleteNode( node2 );
	validlevels = false;
	res = nodes.end();
	res--;
	return res;
}

void Network::Reindex(void)
{
	unsigned int i = 0;
	list<Node>::iterator ndi;

	if ( !indexnodes.empty() ) return;
	indexnodes.clear();
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
		indexnodes.push_back(ndi);
		(*ndi).index = i++;
	}
}

list<Node>::iterator Network::GetNodeByIndex( const unsigned int i )
{
	Reindex();
	if ( i > indexnodes.size()-1 ) throw RuntimeException( "Network::GetNodeByIndex: Wrong index" );
	return indexnodes[i];
}

void Network::SaveMBlif( const char *fn )
// Saves the network to a MBLIF file
{
	ofstream f;
	unsigned int i;
	vector<string>::iterator name_it;
	list<Node>::iterator node_it;
	TermList::iterator ti;

    f.open(fn);
    if (!f.is_open()) throw IOException( "Cannot open file: " + string(fn) );

		// write header
	if ( !model.empty() ) f << ".model " << model << endl;
	f << ".inputs";
	for ( name_it = inames.begin(); name_it != inames.end(); name_it++ )
		f << " " << *name_it;
	f << endl;
	f << ".outputs";
	for ( name_it = onames.begin(); name_it != onames.end(); name_it++ )
		f << " " << *name_it;
	f << endl;

		// write nodes
	for ( node_it = nodes.begin(); node_it != nodes.end(); node_it++ ) {
		f << ".inames";
		for ( i = 0; i < (*node_it).getData()->inputs(); i++ )
			if ( !(*node_it).getData()->getIName(i).empty()) f << " " << (*node_it).getData()->getIName(i);
			else if ( (*node_it).getData()->getData()->front().getCube() == "-" ) f << " " << "[void]";
			else throw RuntimeException( "Network::SaveMBlif: Empty node input name" );
		f << endl;
		f << ".onames";
		for ( i = 0; i < (*node_it).outputs(); i++ )
			if ( !(*node_it).getData()->getOName(i).empty()) f << " " << (*node_it).getData()->getOName(i);
			else throw RuntimeException( "Network::SaveMBlif: Empty node output name" );
		f << endl;

		for ( ti = (*node_it).getData()->getData()->begin(); ti != (*node_it).getData()->getData()->end(); ti++ )
			f << (*ti).getCube() << " " << (*ti).getOM() << endl;
		// f << endl;
	}
	f << ".end" << endl;
}

bool Network::IsSignalThere( const string &n )
// Does the signal with a given name exist in the network?
{
	list<Node>::iterator ndi;
	unsigned int i;

	if ( !signals.empty() ) return ( FindSignal(n).first );
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
		for ( i = 0; i < (*ndi).data.inputs(); i++ )
			if ( n == (*ndi).data.getIName(i) ) return true;
		for ( i = 0; i < (*ndi).data.outputs(); i++ )
			if ( n == (*ndi).data.getOName(i) ) return true;
	}
	return false;
}

bool Network::IsGate( const string &n )
// Does the signal with a given name exist in the network?
{
	list<Node>::iterator ndi;
	unsigned int i;
	pair<bool, list<Signal>::iterator> findres;

	if ( !signals.empty() ) {
		findres = FindSignal(n);
		if ( !findres.first ) return false;
		else if ( findres.second->IsPI() ) return false;
		else if ( !findres.second->IsConstant() ) return false;
		else return true;
	}
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
		for ( i = 0; i < (*ndi).data.outputs(); i++ )
			if ( n == (*ndi).data.getOName(i) ) return true;
	}
	return false;
}

void Network::Rename( const string &oldname, const string &newname )
// Changes the given signal name, in the whole network
{
	list<Signal>::iterator sigit;
	list<Node>::iterator ndi;
	unsigned int i, o;

	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
		for ( i = 0; i < (*ndi).inputs(); i++ )
			if ( (*ndi).data.getIName(i) == oldname ) (*ndi).data.setIName(i, newname);
		for ( o = 0; o < (*ndi).outputs(); o++ ) 
			if ( (*ndi).data.getOName(o) == oldname ) {
				(*ndi).data.setOName(o, newname);
			}
	}
	if ( !signals.empty() ) 
		for ( sigit = signals.begin(); sigit != signals.end(); sigit++ ) {
			if ( (*sigit).name == oldname ) (*sigit).name = newname;
			signalnames.erase(oldname);
			signalnames.insert(newname, sigit);
		}
}

void Network::SpliceNetworks( Network &net )
// splices two networks. Data from net is deleted
{
	indexnodes.clear();
	Disconnect();
	nodes.splice(nodes.end(), net.nodes);
	validlevels = false;
}

list<string> Network::FindVoidSignals(void)
// Returns a list of void (unfed) signals
{
	list<string> voidnodes;
	list<Node>::iterator ndi;
	unsigned int i;
	vector<string>::iterator nameit;
	
	GenerateSignals();
	// for nodes
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		for ( i = 0; i < (*ndi).data.inputs(); i++ )
			if ( !FindSignal( (*ndi).data.getIName(i) ).first ) voidnodes.push_back( (*ndi).data.getIName(i) );
	// for POs
	for ( nameit = onames.begin(); nameit != onames.end(); nameit++ )
		if ( !FindSignal(*nameit).first )
			voidnodes.push_back( *nameit );
	return voidnodes;
}

bool Network::isPI( const string &name )
// is the signal PI?
{
	vector<string>::const_iterator nameit;
	list<Node>::iterator ndi;
	unsigned int i;
	pair<bool, list<Signal>::iterator> findres;

	if ( !signals.empty() ) {
		findres = FindSignal(name);
		if ( findres.first ) return ( (*findres.second).PI );
		else return false;
	}
	else if ( !inames.empty() ) {
		for ( nameit = inames.begin(); nameit != inames.end(); nameit++ )
			if ( name == *nameit ) return true;
		return false;
	}
	else {			// Is it generated by some node?
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
			for ( i = 0; i < (*ndi).outputs(); i++ )
				if ( name == (*ndi).data.getOName(i) ) return false;
		}
		return true;
	}
}

bool Network::isPO( const string &name )
// is the signal PO?
{
	vector<string>::const_iterator nameit;
	list<Node>::iterator ndi;
	unsigned int i;
	pair<bool, list<Signal>::iterator> findres;

	if ( !signals.empty() ) {
		findres = FindSignal(name);
		if ( findres.first ) return ( (*findres.second).PO );
		else return false;
	}
	else if ( !onames.empty() ) {
		for ( nameit = onames.begin(); nameit != onames.end(); nameit++ )
			if ( name == *nameit ) return true;
		return false;
	}
	else {			// Is it not feeding any node?
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
			for ( i = 0; i < (*ndi).data.inputs(); i++ )
				if ( name == (*ndi).data.getIName(i) ) return false;
		}
		return true;
	}
}

bool Network::isConstant( const string &name )
// is the signal fed by a constant node?
{
	vector<string>::const_iterator nameit;
	list<Node>::iterator ndi;
	pair<bool, list<Signal>::iterator> findres;

	if ( !signals.empty() ) {
		findres = FindSignal(name);
		if ( !findres.first ) return false;
		if ( (*findres.second).IsPI() ) return false;
		else return (*findres.second).IsConstant();

	}
	else {			// Find it
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
			if ( (*ndi).inputs() == 1 && (*ndi).outputs() == 1 && (*ndi).getData()->getOName(0) == name )
				if ( (*ndi).getData()->getData()->front().getCube() == "-" ) return true;
	}
	return false;
}

unsigned int Network::getIndex(const list<Node>::iterator ndi)
// returns index of a given node. If not indexed, performs reindexation
{
	if ( indexnodes.empty() ) Reindex();
	return (*ndi).index;
}

unsigned int Network::getLiterals(void)
// returns the number of literals. Only input matrices are considered
{
	list<Node>::iterator ndi;
	unsigned int cnt = 0;

	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		cnt += (*ndi).getData()->getLiterals();
	return cnt;
}

float Network::GetNodeDelay( list<Node>::iterator ndi )
//	how to calculate the node delay
// must be modified for a given technology
{
	unsigned int fanin, fanout;

	fanin = (*ndi).inputs();
	fanout = (*ndi).outputs();

	if ( fanin == 0 || fanout == 0 ) throw RuntimeException( "Network::GetNodeDelay: zero fan-in" );

	// this is for NCL
	if ( fanin == 1 ) return 0;
	if ( fanin != 2 ) throw RuntimeException( "Network::GetNodeDelay: gate not supported" );
	if ( (*ndi).getData()->getTerms() == 1 ) return (float)5.8;
	else if ( (*ndi).getData()->getTerms() == 2 ) return (float)8.6;
	else throw RuntimeException( "Network::GetNodeDelay: gate not supported" ); 

	
/*	
	// This is for dims2
	if ( fanin == 1 ) return 0;
	switch ( fanin ) {
		case 2: return (float)8.2;
		case 3: return (float)15.1;
		case 4: return (float)21.3;
		default: throw RuntimeException( "Network::GetNodeDelay: given fan-in/fan-out not supported" );
	}
*/
/*
	// This is for Nielsen DIMS
	if ( fanin == 1 ) return 0;
	switch ( fanin ) {
		case 2: return (float)8.2;
		case 3: return (float)12.3;
		case 4: return (float)19.4;
		default: throw RuntimeException( "Network::GetNodeDelay: given fan-in/fan-out not supported" );
	}
*/
/*
	// This is for synchronous
	switch ( fanin ) {
		case 1: return 0.6;
		case 2: return 4.0/3.0 + 1.2;
		case 3: return 5.0/3.0 + 1.8;
		case 4: return 6.0/3.0 + 2.4;
		case 5: return 7.0/3.0 + 3.0;
		case 6: return 8.0/3.0 + 3.6;
		default: throw RuntimeException( "Network::GetNodeDelay: given fan-in/fan-out not supported" );
	}
*/
}

float Network::GetDelay(void)
// calculates the circuit delay.
//		may be extended for different gate types
{
	list<Node>::iterator ndi, ndi1;
	unsigned int i;
	bool change;
	pair<bool, list<Signal>::iterator> findres;

	float maxdelay = 0;
	float my_max;

	GenerateSignals();

	// compute nodes delays
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		(*ndi).delay = GetNodeDelay(ndi);

	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		(*ndi).netdelay = -1;

	do {
		change = false;
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
			if ( (*ndi).netdelay == -1 ) {
				my_max = 0;
				for ( i = 0; i < (*ndi).inputs(); i++ ) {
					if ( !isPI((*ndi).getData()->getIName(i)) && !isConstant((*ndi).getData()->getIName(i)) ) {
						findres = FindSignal( (*ndi).getData()->getIName(i) );
						if ( !findres.first ) throw RuntimeException( "Network::GetDelay: Undefined signal" );
						ndi1 = (*findres.second).GetSource()->first;
						if ( (*ndi1).netdelay == -1 ) {
							my_max = -1;
							break;
						} else if ( (*ndi1).netdelay > my_max ) my_max = (*ndi1).netdelay;
					}
				}
				if ( my_max != -1 ) {
					(*ndi).netdelay = my_max + (*ndi).delay;
					change = true;
					if ( (*ndi).netdelay > maxdelay ) maxdelay = (*ndi).netdelay;
				}
			}
	} while (change);
	return maxdelay;
}

float Network::GetNCLDelay(void)
// calculates the circuit delay according NCL. To be deleted.
//		may be extended for different gate types
{
	list<Node>::iterator ndi, ndi1;
	unsigned int i;
	float mydelay;
	bool change;
	pair<bool, list<Signal>::iterator> findres;

	float maxdelay = 0;
	float my_max;

	GenerateSignals();

		// compute nodes delays
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		(*ndi).delay = GetNodeDelay(ndi);

	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		(*ndi).netdelay = -1;

	do {
		change = false;
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
			if ( (*ndi).netdelay == -1 ) {
				my_max = 0;
				for ( i = 0; i < (*ndi).inputs(); i++ ) {
					if ( !isPI((*ndi).getData()->getIName(i)) && !isConstant((*ndi).getData()->getIName(i)) ) {
						findres = FindSignal( (*ndi).getData()->getIName(i) );
						if ( !findres.first ) throw RuntimeException( "Network::GetDelay: Undefined signal" );
						ndi1 = (*findres.second).GetSource()->first;
						if ( (*ndi1).netdelay == -1 ) {
							my_max = -1;
							break;
						} else {
							if ( (*ndi).getData()->getTerms() == 1 && (*ndi).getData()->getData()->front().isLit_1(i) )
								mydelay = (*ndi1).netdelay + (float)3.8;
							else mydelay = (*ndi1).netdelay;
							if ( mydelay > my_max ) my_max = mydelay;
						}
					}
				}
				if ( my_max != -1 ) {
					(*ndi).netdelay = my_max + (*ndi).delay;
					change = true;
					if ( (*ndi).netdelay > maxdelay ) maxdelay = (*ndi).netdelay;
				}
			}
	} while (change);
	return maxdelay;
}

float Network::GetMinDelay(void)
// returns the minimum circuit delay
// GetDelay must be called first
{
	list<Node>::iterator ndi;
	unsigned int i;

	float mindelay;

	mindelay = FLT_MAX;
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		for ( i = 0; i < (*ndi).outputs(); i++ )
			if ( isPO((*ndi).getData()->getOName(i)) )
				if ( mindelay > (*ndi).netdelay && (*ndi).netdelay != 0 ) mindelay = (*ndi).netdelay;

	return mindelay;
}

float Network::GetAvgDelay(void)
// returns the average circuit delay
// GetDelay must be called first
{
	list<Node>::iterator ndi;
	unsigned int i, cnt;

	float avgdelay;

	avgdelay = 0;
	cnt = 0;
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		for ( i = 0; i < (*ndi).outputs(); i++ )
			if ( isPO((*ndi).getData()->getOName(i)) )
				if ( (*ndi).netdelay != 0 ) {
					avgdelay += (*ndi).netdelay;
					cnt++;
				}
	avgdelay /= cnt;

	return avgdelay;
}

pair<bool, list<Signal>::iterator> Network::FindSignal( const string &n )
// finds signal with a given name
// the first pair value is true, if found. Then the iterator follows
{
	list<Signal>::iterator sigit;

	if ( signals.empty() ) GenerateSignals();
	return signalnames.find(n);
}

bool Network::DeleteSignal( const string &n )
// deletes a signal with a given name
// returns true, if succeeded
// may cause inconsistency between the network and signals structure
{
	list<Signal>::iterator sigit;
	pair<bool, list<Signal>::iterator> findres;

	findres = FindSignal(n);
	if ( !findres.first ) return false;
	signals.erase(findres.second);
	signalnames.erase(n);
	return true;
}

list<Signal>::iterator Network::FindOrCreateSignal( const string &name )
// If the signal exists, returns the pointer to it. If not, it is created
{
	Signal sig;
	pair<bool, list<Signal>::iterator> res;
	list<Signal>::iterator sigit;

	if ( !signals.empty() ) res = FindSignal(name);
	if ( signals.empty() || !res.first ) {				// can be both PI and PO
		signals.push_back(sig);
		sigit = signals.end(); sigit--;
		(*sigit).owner = this;
		(*sigit).name = name;
		signalnames.insert(name, sigit);
	} else sigit = res.second;
	return sigit;
}

void Network::AddNodeSignals( list<Node>::iterator ndi )
{
	unsigned int i;
	list<Signal>::iterator sigit;

	for ( i = 0; i < (*ndi).outputs(); i++ ) {
		sigit = FindOrCreateSignal( (*ndi).getData()->getOName(i) );
		if ( !(*sigit).undefsource ) throw RuntimeException( "Network::GenerateSignals: Multiple net drivers" );
		(*sigit).source = make_pair(ndi, i);
		(*sigit).undefsource = false;
	}
	for ( i = 0; i < (*ndi).inputs(); i++ ) {
		sigit = FindOrCreateSignal( (*ndi).getData()->getIName(i) );
		(*sigit).dest.push_back( make_pair(ndi, i) );
	}
}

void Network::GenerateSignals(void)
// Generates the signals structure
{
	list<Node>::iterator ndi;
	unsigned int i;
	list<Signal>::iterator sigit;

	if ( !signals.empty() ) return;
	
		// Process PIs
	for ( i = 0; i < inames.size(); i++ ) {
		sigit = FindOrCreateSignal( inames[i] );
		(*sigit).PI = true;
	}
		// Process POs
	for ( i = 0; i < onames.size(); i++ ) {
		sigit = FindOrCreateSignal( onames[i] );
		(*sigit).PO = true;
	}	
		// process nodes
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) AddNodeSignals(ndi);
}

void Network::DerivePorts(void)
// Generates PIs and POs by analyzing the network.
// Original PIs and POs are lost, new PIs are created as un-fed nodes, POs as nodes with no fanout
// The "signals" structure is updated
{
	list<Signal>::iterator sigit;
	
	inames.clear();
	onames.clear();

	signals.clear();
	signalnames.clear();
	GenerateSignals();

	for ( sigit = signals.begin(); sigit != signals.end(); sigit++ ) {
		(*sigit).PI = (*sigit).undefsource;
		(*sigit).PO = (*sigit).dest.empty();
		if ( (*sigit).PI ) inames.push_back( (*sigit).name );
		if ( (*sigit).PO ) onames.push_back( (*sigit).name );
	}
}

void Network::AddInput( const string &name )
// Adds an input, if not present
{
	list<Signal>::iterator sigit;

	inames.push_back(name);
	if ( !signals.empty() ) {
		sigit = FindOrCreateSignal( name );
		(*sigit).PI = true;
	}
}

void Network::AddOutput( const string &name )
// Adds an input, if not present
{
	list<Signal>::iterator sigit;

	onames.push_back(name);
	if ( !signals.empty() ) {
		sigit = FindOrCreateSignal( name );
		(*sigit).PO = true;
	}
}

list<Signal> *Network::GetSignals(void)
{
	if ( signals.empty() ) GenerateSignals();
	return &signals;
}

void Network::ReconnectInput( list<Node>::iterator ndi, unsigned int pos, const string &name )
// Changes name of a given input
// Makes changes in signals structure as well
{
	list<Signal>::iterator sigit;
	string oldname;
	list<pair<list<Node>::iterator, unsigned int> >::iterator destit;

	oldname = (*ndi).getData()->getIName( pos );
	(*ndi).getData()->setIName( pos, name );
	if ( !signals.empty() ) {
		// Make (connect) new signal	
		sigit = FindOrCreateSignal(name);
		(*sigit).dest.push_back( make_pair(ndi, pos) );
		// Disconnect old signal
		for ( sigit = signals.begin(); sigit != signals.end(); sigit++ ) {
			destit = (*sigit).dest.begin();
			while ( destit != (*sigit).dest.end() ) {
				if ( (*destit).first == ndi ) destit = (*sigit).dest.erase(destit);
				else destit++;
			}
		}
	}
}

void Network::ReconnectOutput( list<Node>::iterator ndi, unsigned int pos, const string &name )
// Changes name of a given output
// Makes changes in signals structure as well
{
	list<Signal>::iterator sigit;
	string oldname;
	list<pair<list<Node>::iterator, unsigned int> >::iterator destit;
	pair<bool, list<Signal>::iterator> findres;

	oldname = (*ndi).getData()->getOName( pos );
	(*ndi).getData()->setOName( pos, name );
	if ( !signals.empty() ) {
		// Make (connect) new signal	
		sigit = FindOrCreateSignal(name);
		if ( !(*sigit).undefsource ) throw RuntimeException( "Network::ReconnectOutput: Multiple net drivers" );
		(*sigit).source = make_pair(ndi, pos);
		(*sigit).undefsource = false;
		// Disconnect old signal
		findres = FindSignal( name );
		if ( !findres.first ) throw RuntimeException( "Network::ReconnectOutput: Unknown signal source" );
		(*findres.second).undefsource = true;
	}
}

void Network::ComputeLevels(void)
// calculates the circuit delay.
//		may be extended for different gate types
// returns the circuit levels
{
	list<Node>::iterator ndi, ndi1;
	unsigned int i, my_max;
	bool change;
	pair<bool, list<Signal>::iterator> findres;

	if ( validlevels ) return;

	GenerateSignals();

	levels = 0;
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		(*ndi).level = 0;

	do {
		change = false;
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
			if ( (*ndi).level == 0 ) {
				my_max = 0;
				for ( i = 0; i < (*ndi).inputs(); i++ ) {
					if ( !isPI((*ndi).getData()->getIName(i)) && !isConstant((*ndi).getData()->getIName(i)) ) {
						findres = FindSignal( (*ndi).getData()->getIName(i) );
						if ( !findres.first ) throw RuntimeException( "Network::ComputeLevels: Undefined signal" );
						ndi1 = (*findres.second).GetSource()->first;
						if ( (*ndi1).level == 0 ) {
							my_max = UINT_MAX;
							break;
						} else if ( (*ndi1).level > my_max ) my_max = (*ndi1).level;
					}
				}
				if ( my_max != UINT_MAX ) {
					(*ndi).level = my_max + 1;
					change = true;
					if ( (*ndi).level > levels ) levels = (*ndi).level;
				}
			}
	} while (change);
	validlevels = true;
}

unsigned int Network::GetNodeLevel( list<Node>::iterator ndi )
// Returns the level of the node
{
	if ( !validlevels ) ComputeLevels();
	return (*ndi).level;
}

unsigned int Network::GetLevels( void )
// returns the number of circuit levels
{
	if ( !validlevels ) ComputeLevels();
	return levels;
}

string Network::getIName( const unsigned int i ) const
{
	if ( inames.empty() ) return "";
	if ( i >= inames.size() ) throw RuntimeException( "Network::getIName: Wrong variable index" );
	return inames[i];
}

string Network::getOName( const unsigned int i ) const
{
	if ( onames.empty() ) return "";
	if ( i >= onames.size() ) throw RuntimeException( "Network::getOName: Wrong variable index" );
	return onames[i];
}

void Network::setIName( const unsigned int i, const string name )
{
	if ( inames.empty() ) inames.resize(i+1);
	if ( i >= inames.size() ) throw RuntimeException( "Network::setIName: Wrong variable index" );
	inames[i] = name;
}

void Network::setOName( const unsigned int i, const string name )
{
	if ( onames.empty() ) onames.resize(i+1);
	if ( i >= onames.size() ) throw RuntimeException( "Network::setOName: Wrong variable index" );
	onames[i] = name;
}
