/*************************************************************** 
	The GPDD Project (GPDD1) 
 
	GPDD stands for "Graph-Pair Decision Diagram" 
 
Contributors: 
 
   CHEN, Weiwei		(MS 2007 graduate) 
   MENG, Xiaoxuan		(MS 2009 graduate) 
   ZENG, Mei			(MS 2010 graduate) 
   LI, Ji				(MS 2010 graduate) 
   MA, Diming			(MS 2011 graduate) 
 
   SHI, Guoyong 		(Prof)  shiguoyong@ic.sjtu.edu.cn 
 
   School of Microelectronics 
   Shanghai Jiao Tong University 
   (c) 2005 - 2010 
******************************************************************/ 

/**
	main.cpp
   	08-27-2010; 
*/


#include <cstdio>
#include <cstddef>
#include <cmath>
#include <cfloat>
#include <cctype>
#include <cstring>
#include <cassert>

#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <stack>
#include <ctime>
#include <vector>
#include <limits>

#include "option.h"		// Defining the compiler directives
#include "gpdd_global.h"
#include "complex.h"		
#include "utility.h"

#include "ToGraph.h"
#include "GPDD.h"

#include "Analysis_AC.h"
//#include "Analysis_PZ.h"
//#include "Analysis_Sens.h"


using std::istream;
using std::printf;
using std::size_t;
using std::scientific;
using std::vector;
using std::log10;
using std::pow;
using std::abs;
//using std::arg;
using std::sqrt;
using std::exp;
using std::numeric_limits;
using std::ifstream;
using std::ofstream;
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::flush;
using std::string;
using std::map;
using std::setw;
using std::setprecision;
using std::ios;
using std::istringstream;
using std::toupper;
using std::getline;
using std::fabs;
using std::atof;
using std::strcmp;


namespace
{
double gain_trigger_phase_margin = 0.0; // dB
double gain_trigger_band_width = 0.0; // dB
double freq_start = 1.0e-6, freq_end = 1.0e9; // Hz
//double freq_start = 1.0e-6, freq_end = 1.0e7; // Hz
int num_each_tf_samples = 50;

inline void string_to_upper(string &str)
{
	for (string::iterator it = str.begin(); it != str.end(); ++it)
		*it = toupper(*it);
}

template<typename T>
void swap(T &a, T &b)
{
	T tmp(a);
	a = b;
	b = tmp;
}

} // unamed namespace



GPDD			*gpdd = NULL;

ToGraph			*toGraph = new ToGraph;
// The global <toGraph> also is used by the parser "netgram.g". 
// So don't change the variable name. (G. Shi)


#ifdef PRINT_TO_FILE
ofstream dataFile;
ofstream dataFile_single;
ofstream sensFileMag("SensMag.dat");
ofstream sensFilePhase("SensPhase.dat");
ofstream freqResonse2Dfile("FreqRes2D.dat");
#endif // PRINT_TO_FILE

// Dump help information on screen.
inline void help_info(char *program)
{
	cout << "Synopsis:" << endl;
	cout << program << " netlist[.cir]" << endl;
}



int main(int argc, char *argv[])
{
	
/********************************************************************/
/******************************* Parsing Netlist **************************/
/*********************************************************************/

	/*char		*file_in = 0, *file_out = 0, *file_data = 0*/;
	string file_in;
	bool have_configure_file = false;

	Analysis_AC 		*analysis_ac = NULL;
//	Analysis_PZ		*analysis_pz = NULL;
//	Analysis_Sens 	*analysis_sens = NULL;

	std::vector<Analysis_AC *> 	AC_list;

	// Check each command line argument
	for (int i = 1; i < argc; ++i)
	{
		// Use the configure file instead of interactive input if it exists.
		if (!strcmp(argv[i], "-c"))
		{
			++i; // Next argument must be of "-c".
			if (i == argc)
			{
				cerr << "Missing argument for \"-c\"!" << endl;
				help_info(argv[0]);
				return EXIT_FAILURE;
			}
			ifstream ifstrm(argv[i]);
			if (!ifstrm)
			{
				cerr << "Cannot open configure file: " << argv[i] << endl;
				return EXIT_FAILURE;
			}
		}
		else
		{
			// Get the input file name and make an output file name.
			file_in = string(argv[i]);
		}
	}


	string tmp_str = file_in;
	
	int loc = tmp_str.find('.');

	// If no extension is found,
	if (loc  == -1) 	// no extension
	{
		cout << LINE_INFO << "No extension in file name." << endl;
		tmp_str.append(".sp");
		file_in += ".sp";
	} 
	else if (tmp_str.compare(loc, 4, ".cir") == 0)
	{
		cout << LINE_INFO << "File extension is [.cir]." << endl;
		tmp_str.replace(loc, 4, ".gph");
	}
	else if (tmp_str.compare(loc, 3, ".sp") == 0)
	{
		cout << LINE_INFO << "File extension is [.sp]." << endl;
		tmp_str.replace(loc, 4, ".gph");
	}
	else 
	{
		cout << LINE_INFO << "File extension is not allowed." << endl;
	}


	// Output File: Inputfile + ".gph"
	//string file_out = file_in + ".gph";
	string file_out = tmp_str;

	cout << "\n Input file name: " << file_in
		<< "\nOutput file name: " << file_out << endl;


	cout << LINE_INFO << "Reading the netlist ..." << endl;
	toGraph->readNetlist(file_in.c_str());

	
#ifdef PRINT_TO_FILE
	ofstream	outFile(file_out.c_str());

	string file_data = file_in + ".out";
	dataFile.open(file_data.c_str());
	string file_data_single = file_in + ".s_s";
	dataFile_single.open(file_data_single.c_str());
#endif // PRINT_TO_FILE

	cout << LINE_INFO << "Run to here." << endl << flush;

	gpdd = new GPDD(toGraph->getNumNodes(), 
					 toGraph->getNumEdges(), 
					 toGraph->getNumMOSDevices(),
					 toGraph->getEdgeList());

//	cout << LINE_INFO << "Code is here." << endl << flush;
	
	cout << LINE_INFO << "GPDD construction done." << endl;


#ifdef PRINT_GRAPH_INFO_
	cout << LINE_INFO << "Print symbols: ";
	gpdd->PrintSymbols();
	
	gpdd->SetNewValue("R6", 1.376E-6);	// Li Ji  in testing
	gpdd->SetNewValue("R1", 1.376E-6);
	
	gpdd->PrintSymbols();

	cout << LINE_INFO << "Print circuit statistics ..." << endl;
	gpdd->PrintCircuitStatistics();
#endif

	

/************************************************************************/
/*************************** Construcing GPDD *******************************/
/************************************************************************/

	cout << LINE_INFO << "\n\t-> Start Graph-Pair Decision Diagram (GPDD) construction ..." << endl;
//	cout << "Stats: " << RunStats() << endl;
	cout << endl;

	double start_time, end_time, elapsed_time;

	gpdd->InitGPDDroot();

	start_time = clock();

#ifdef RECURSIVE_CONSTRUCTION_
	cout << LINE_INFO << "\n\tRunning RECURSIVE GPDD construction ..." << endl;
	gpdd->GraphReduction();
#else	//  NON_RECURSIVE_CONSTRUCTION
	cout << LINE_INFO << "\n\tRunning NON-RECURSIVE GPDD construction ..." << endl;
	gpdd->GraphReduction_NR();
#endif 
	
	end_time = clock();
	cout << LINE_INFO <<"Construction time used = " << 1.0*(end_time - start_time)/1000000 << endl;
	
	// cout << "\nStats: " << RunStats();
	// cout << endl;

	cout << LINE_INFO << "Calling GPDD::Reduce() ..." << endl;
	if (gpdd->Reduce() == STOP)
	{
		cout << LINE_INFO << "\n\tFailed to reduce GPDD." << endl;
		return (0);
	}
	
	// cout << "\nStats: " << RunStats();
	// cout << endl;
	
	cout << LINE_INFO << "Calling GPDD::GC() for garbage collection ..." << endl;
	gpdd->GC();

	cout << LINE_INFO << "\n\t-> Finished GPDD construction\n" << endl;

/***************************************************************/
/************************ Displaying  GPDD ***************************/
/****************************************************************/

#ifdef PRINT_GPDD_TERMS_
	cout << LINE_INFO << "Print product terms ..." << endl;
	gpdd->PrintTerms(cout);
#endif

#ifdef EXPAND_LUMPED_GPDD 

	cout << LINE_INFO << "\n\t--> Expand GPDD lumped nodes ..." << endl;
	// s-expand: construct muti-root trees. Li Ji
	
	// Must expand the lumped GPDD before running s-expansion to get the s-coefficients.
	if (gpdd->ExpandLumpedGPDD() == STOP) {
		cout << LINE_INFO << "\n\tFailed to expand lumped GPDD." << endl;
		return (0);
	}

	cout << LINE_INFO << "\n\t<-- End of GPDD lump expansion" << endl;

#endif // EXPGPDD 

#ifdef CALCULATE_S_COEFFICIENTS 
	cout << LINE_INFO << "\n\t--> Begin GPDD s-coefficient expansion ..." << endl;

	// Extract the s-coefficients of the transfer function
	gpdd->sExpandGPDD();

	cout << LINE_INFO << "\n\t<-- End of s-coefficient expansion" << endl;
	
///	gpdd->CountNumTermsForAllCoeff();

#endif  

#ifdef	PRINT_S_COEFFICIENTS_
	cout << LINE_INFO << "Print s-coefficients ..." << endl;
	gpdd->Print_sPolynomialCoeffs(cout);
#endif


#ifdef PRINT_TRANSFER_FUNCTION_
	cout << LINE_INFO << "Print the Transfer Function H(s) ... " << endl;
	gpdd->PrintTransferFunction(cout);
	cout << LINE_INFO << "Finish printing H(s)" << endl;
#endif


#ifdef DISPLAY_SYMBOL_STATISTICS
	cout << LINE_INFO << "Collect GPDD symbol statistics ... " << endl;
	gpdd->RunSymbolStatistics();
#endif

#ifdef PRINT_GPDD_NODES_
	cout << LINE_INFO << "Print GPDD vertex structure ... " << endl;
	gpdd->PrintGPDD(cout);
#endif

	// Display the statistics of the GPDD
#ifdef DISPLAY_GPDD_STATISTICS
	cout << LINE_INFO << "Show GPDD overall statistics ... " << endl;
	gpdd->PrintGPDDStatistics();
#endif   // DISPLAY_STATISTICS


/***************************************************************/
/************************ Testing Analysis ***************************/
/****************************************************************/

	AC_list = toGraph->GetAnalysisList();

	analysis_ac = AC_list[0];	// at least one analysis exists

	if (!analysis_ac) {
		cout << LINE_INFO << "Did not find AC analysis. " << endl;
		return (0);
	}

	analysis_ac->SetGPDD(gpdd);


#ifdef RUN_AC_ANALYSIS 
	cout << LINE_INFO << "Begin GPDD Evaluation ... ..." << endl;

	start_time = clock();
	
	if ( analysis_ac->RunACAnalysis() == STOP ) {
		return (0);
	}
	
	end_time = clock();
	elapsed_time =(end_time - start_time )/1000000;
	
	cout << LINE_INFO << "End GPDD Evaluation ... ..." << endl;
	cout << LINE_INFO << "Time used : " << elapsed_time << " sec" << endl;
#endif

#ifdef  TEST_DIFFERENCE_SENSITIVITY	
	cout << LINE_INFO << "\n\tTest difference sensitivity computation ..." << endl;
	
	analysis_ac->DifferenceSensitivity("R1", 0.01); 
	
	cout << LINE_INFO << "End of  difference sensitivity computation" << endl;
#endif


#ifdef PRINT_TO_FILE
	sensFileMag.close();
	sensFilePhase.close();
	freqResonse2Dfile.close();
#endif

	//  Destruct the GPDD object
	cout << LINE_INFO << "Start destructing GPDD ..." << endl;
	delete gpdd;
	
	cout << LINE_INFO << "GPDD deconstruction done" << endl;
	
///	delete analysis_pz;
///	delete analysis_sens;
	delete analysis_ac;

	// ToGraph class maintains a list of analyses. So, deconstruct at the end!
	delete toGraph;
	toGraph = NULL;

	cout << LINE_INFO << argv[0] << " exits normally." << endl;
	
	return 0;
}

