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

#ifdef _MSC_VER
#pragma hdrstop
#endif

using namespace std;

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

void Help()
{
	cout << "Syntax: Parity source.blif [-g] dest.blif [max parity fan-in]" << endl;
	cout << "Options:" << endl;
	cout << "          -g groufile    Create parity according the group file" << endl;
}

string ToBin( unsigned int num, unsigned int l )
{
	unsigned int pos;
	string res;

	res.assign(l, '0');
	pos = l-1;
	do {
		if ( num%2 == 1 ) res[pos] = '1';
		num /= 2;
		pos--;
	} while ( num > 0 );
	return res;
}

void ConstructParity( SoPLA &pla )
// fills SoPLA with parity
{
	unsigned long counter, endval;
	unsigned int i;
	string s;
	bool par;

	endval = (unsigned long)(1 << pla.inputs());
	for ( counter = 1; counter < endval; counter++ ) {
		s = ToBin( counter, pla.inputs() );
		par = false;
		for ( i = 0; i < pla.inputs(); i++ )
			if ( s[i] == '1' ) par = !par;
		if ( par ) pla.AppendTerm(s);
	}
}

Blif *AddParity(Blif &source, unsigned int fanin)
{
	list<SoPLA>::iterator pla_it;
	Blif *dest;
	vector<string> output;
	SoPLA *parity;
	unsigned int i, j;
	unsigned int tmps;
	string num;
	ostringstream ss;

	output.push_back("parity");
	dest = new Blif( source.getINames(), output );
	dest->SetModel(source.GetModel());

	for ( pla_it = source.getData()->begin(); pla_it != source.getData()->end(); pla_it++ )
		dest->AppendPLA(&*pla_it);

	i = source.outputs();
	tmps = 0;
	while ( i != 0 ) {
		if ( fanin == 0 || i < fanin ) {
			if ( tmps == 0 ) parity = new SoPLA(i, true);
			else parity = new SoPLA(i+1, true);
		} else parity = new SoPLA(fanin, true);
		ConstructParity(*parity);
		if ( tmps == 0 && (fanin == 0 || i <= fanin) ) {	// all outputs fit, no temporary
			for ( j = 0; j < i; j++ )
				parity->setIName(j, source.getOName(j));
			parity->setName("parity");
			i = 0;
		} else if ( tmps > 0 ) {			// intermediate XOR (or last)
			ss << tmps-1;
			num = ss.str();
			parity->setIName( 0, "xor_tmp_"+num );
			j = 1;
			do {
				parity->setIName(j, source.getOName(source.outputs()-i));
				i--;
				j++;
			} while ( j < fanin && i > 0 );
			if ( i > 0 ) {
				ss << tmps++;
				num = ss.str();
				parity->setName("xor_tmp_"+num);
			} else parity->setName("parity");
		} else {							// first XOR
			for ( j = 0; j < fanin; j++ )
				parity->setIName(j, source.getOName(j));
			i -= fanin;
			ss << tmps++;
			num = ss.str();
			parity->setName("xor_tmp_"+num);
		}
		dest->AppendPLA(parity);
	}
	return dest;
}

Blif *AddParity(Blif &source, unsigned int fanin, list< list<string> > &grouping)
// not done yet
{
	list<SoPLA>::iterator pla_it;
	Blif *dest;
	vector<string> output;
	SoPLA *parity;
	unsigned int i, j, gr_no;
	unsigned int tmps;			// intermediate XORs
	string num, num1;
	list<string> group;
	list< list<string> >::iterator grit;
	list<string>::iterator groupit;
	string parity_name;
	ostringstream ss;

	// generate output (parity) signals
	for ( i = 0; i < grouping.size(); i++ ) {
		ss << i;
		num = ss.str();
		parity_name = "parity_" + num;
		output.push_back(parity_name);
	}
	// create new Blif
	dest = new Blif( source.getINames(), output );
	dest->SetModel(source.GetModel());

	// copy the original nodes
	for ( pla_it = source.getData()->begin(); pla_it != source.getData()->end(); pla_it++ )
		dest->AppendPLA(&*pla_it);

	gr_no = 0;
	for ( grit = grouping.begin(); grit != grouping.end(); grit++ ) {
		ss << gr_no;
		num = ss.str();
		parity_name = "parity_" + num;
		tmps = 0;
		i = (*grit).size();
		while ( i != 0 ) {
			if ( fanin == 0 || i < fanin ) {
				if ( tmps == 0 ) parity = new SoPLA(i, true);
				else parity = new SoPLA(i+1, true);
			} else parity = new SoPLA(fanin, true);
			ConstructParity(*parity);
			if ( tmps == 0 && (fanin == 0 || i < fanin) ) {	// all outputs fit, no temporary
				j = 0;
				for ( groupit = (*grit).begin(); groupit != (*grit).end(); groupit++ )
					parity->setIName(j++, (*groupit));
				parity->setName(parity_name);
				i = 0;
			} else if ( tmps > 0 ) {			// intermediate XOR (or last)
				ss << tmps-1;
				num = ss.str();
				parity->setIName( 0, "xor_tmp_"+parity_name+"_"+num );
				j = 1;
				do {
					parity->setIName(j, (*groupit++));
					i--;
					j++;
				} while ( j < fanin && i > 0 );
				if ( i > 0 ) {
					ss << tmps++;
					num = ss.str();
					parity->setName("xor_tmp_"+parity_name+"_"+num);
				} else parity->setName(parity_name);
			} else {							// first XOR
				groupit = (*grit).begin();
				for ( j = 0; j < fanin; j++ )
					parity->setIName(j, (*groupit++));
				i -= fanin;
				ss << tmps++;
				num = ss.str();
				parity->setName("xor_tmp_"+parity_name+"_"+num);
			}
			dest->AppendPLA(parity);
		}
		gr_no++;
	}

	return dest;
}

int main(int argc, char* argv[])
{
    Blif source;
    Blif *dest;
	char *src_file = NULL;
	char *dest_file = NULL;
	unsigned int fanin = 0;
	int i;
//	unsigned int n = 1;
	char *groupfilen = NULL;
	list< list<string> > grouping;
	ifstream groupfile;
	stringstream ss;
	char line[1000];
	list<string> group;
	string s;


	cout << "Parity Generator 2.2, (c) June 2010 by Petr Fiser" << endl;
	if ( argc < 3 ) {
        Help();
        exit(1);
    }

	try {
		i = 1;
		while ( i != argc ) {
			if ( argv[i][0] != '-' && src_file == NULL ) src_file = argv[i];
			else if ( argv[i][0] != '-' && dest_file == NULL ) dest_file = argv[i];
			else if ( argv[i][0] != '-' && src_file != NULL && dest_file != NULL ) fanin = atoi(argv[i]);
/*			else if ( strcmp( argv[i], "-n" ) == 0 ) {
				i++;
				n = atoi(argv[i]);
			}
*/			else if ( strcmp( argv[i], "-g" ) == 0 ) {
				i++;
				groupfilen = argv[i];
			}

			else {
				Help();
				exit(1);
			}
			i++;
		}
	} catch (...) {
        Help();
        exit(1);
    }

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

	if ( fanin != 0 && fanin < 2 ) {
        Help();
        exit(1);
    }

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

	if ( groupfilen == NULL ) dest = AddParity(source, fanin);
	else {
		groupfile.open(groupfilen);
		while ( !groupfile.eof() ) {
			groupfile.getline( line, 1000 );
			if ( line[0] == 0 ) break;
			ss.clear();
			ss << line;
			group.clear();
			while ( !ss.eof() ) {
				ss >> s;
				group.push_back(s);
			}
			grouping.push_back(group);
		}
		groupfile.close();
		dest = AddParity(source, fanin, grouping);
	}

	dest->SaveBlif(dest_file);
	delete dest;
	return 0;
}
