void BASE::log(const char* fmt, ... )
{
	// check if time is initializce
	auto now = std::chrono::system_clock::now();

	if (time_start==0) 
		time_start = std::chrono::system_clock::to_time_t(now);

	// insert time stamp
	char tname[2000];
	timestamp(tname, time_start);
	
	if (plot_memory_usage)
	{
		long cm = getSelfPhysicalMemory();
		if (cm > peak_memory) peak_memory=cm;
		long display_cm, display_peak;
		char c1=' ',c2=' ';
		if (cm > 1024) { display_cm = cm/1024; c1='k'; }
		if (cm > 1024*1024) { display_cm = cm/1024/1025; c1='M'; }
//		if (cm > 1024*1024*1024) { display_cm = cm/1024/1024/1024; c1='G'; }
		if (peak_memory > 1024) { display_peak = peak_memory/1024; c2='k'; }
		if (peak_memory > 1024*1024) { display_peak= peak_memory/1024/1025; c2='M'; }
//		if (peak_memory > 1024*1024*1024) { display_peak = peak_memory/1024/1024/1024; c2='G'; }
		sprintf(tname, "%s[mem:%7ld %cB peak:%7ld %cB] ",tname,display_cm,c1,display_peak,c2);
	}

	char txt[1024];
	va_list args;
	va_start(args, fmt);
	vsprintf(txt, fmt, args);
	va_end(args);

	mkdir(target_dir,0777);
	char logname[1000];
	sprintf(logname,"%s/log_%d", target_dir,getpid());
	FILE*f = fopen(logname,"at");
	fprintf(f, "%s", tname);
	fprintf(f,txt);
	fprintf(f,"\n");
	fclose(f);

	printf("%s", tname);
	printf(txt);
	printf("\n");
	fflush(stdout);
}

bool BASE::file_exists(const std::string &s)
{
	struct stat buffer;
	return (stat (s.c_str(), &buffer) == 0);
}

bool BASE::mark_exists(const char* fmt, ... )
{
	char txt[1024];
	va_list args;
	va_start(args, fmt);
	vsprintf(txt, fmt, args);
	va_end(args);

	return (file_exists(txt)) ? true : false;
}

bool BASE::mark(const char* fmt, ... )
{
	char txt[1024];
	va_list args;
	va_start(args, fmt);
	vsprintf(txt, fmt, args);
	va_end(args);

	if (file_exists(txt))
	{
		return true;
	} else
	{
		ofstream fout(txt);
		fout << getpid() <<endl;
		fout.close();
		usleep(5000);
		int ipid;
		ifstream fin(txt);
		fin >> ipid;
		fin.close();
		if (ipid!=getpid()) return true;
		return false;
	}
	return false;
}

void BASE::create_mark(const char* fmt, ... )
{
	char txt[1024];
	va_list args;
	va_start(args, fmt);
	vsprintf(txt, fmt, args);
	va_end(args);
	if (!file_exists(txt))
	{
		int fd = open(txt, O_CREAT | O_RDWR, 0666);
		close(fd);
	}
}

void BASE::timestamp(char *str, time_t reference)
{
	std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

	// convert from C++11 API to the C struct (milliseconds are dropped)
//		time_t secs = std::chrono::system_clock::to_time_t(now) - reference;
	// reconstruct time 
	struct tm *date = std::localtime(&reference);
	auto last_sec = std::chrono::system_clock::from_time_t(std::mktime(date));
	int milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_sec).count();


//		struct tm *date = std::localtime(&secs);

	// number of milliseconds since the last whole seconds (must be in C++11)
//		auto last_sec = std::chrono::system_clock::from_time_t(std::mktime(date));
//		int milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_sec).count();

//		sprintf(str, "%04d|%02d|%02d %02d:%02d:%02d.%03d", date->tm_year+1900, date->tm_mon+1, date->tm_mday, date->tm_hour, date->tm_min, date->tm_sec, milliseconds);
//		sprintf(str, "[t%10d ms] ", milliseconds);
	sprintf(str, "[%13.1fs] ", milliseconds/1000.0);
}


std::string BASE::ReplaceString(std::string subject, const std::string& search, const std::string& replace)
{
	size_t pos = 0;
	while ((pos = subject.find(search, pos)) != std::string::npos)
	{
		subject.replace(pos, search.length(), replace);
		pos += replace.length();
	}
	return subject;
}

bool BASE::sign(ex nr)
{
	for (int i=0;i<nr.nops();i++)
	if (!is_a<add>(nr.op(i)))
	if (is_a<numeric>(nr.op(i)))
	if (nr.op(i)<0) return true;
	return false;
}

string BASE::factorize_latex(ex co)
{
	ostringstream s;
	s << latex;
	// prepare co
	ex co1 = subs( co, subs_vels );
	ex r = GiNaC::normal( co1 );
	ex nr = GiNaC::expand(numer(r));
	ex dr = denom(r);
	// decomposition
	ex common=(ex)1;
	ex rest=(ex)1;
	bool negative = false;
	
	if (is_a<add>(nr))
	{	
		// convert to poly
		exmap mp;
		ex p = nr.to_polynomial(mp);
//			ex nrpoly = GiNaC::subs(p,mp);
//			ex nrpoly=GiNaC::subs(GiNaC::factor(p),mp);
		
//			cout << "coef nr:"<<nr<<endl;
//			cout << "p "<<p<<endl;

		common = (ex)1;
		
		// first op will determine atomic parts of the factorization
//			ex first = p.op(0);
//			ex second = p.op(1);
//			cout << "first term: "<<first<<endl;
//			cout << "second term: "<<second<<endl;
		for (int i=1;i<p.nops();i++)
		{
			ex c1 = gcd(p.op(0), p.op(i));
			common = (common==1) ? c1 : gcd(common, c1);
//				cout << "c1: "<<c1<<endl;
		}
		common = GiNaC::subs(common, mp);
		rest = GiNaC::expand(nr/common);
//			cout << "common: "<<common<<endl;
//			cout << "rest: "<<rest<<endl;
		negative = sign(common);
		
	} else
	{
		// determine sign of an expression
		negative = sign(nr);
		common=nr;
		rest=(ex)1;
	}
	
	if (negative) { common = -common; rest=-rest; }

	// test
	ex test = GiNaC::expand(common*rest - nr);
	if (test!=0)
	{
		cout << "error: test = "<<test<<endl;
		cout << "       nr   = "<<nr<<endl;
		cout << "       c*r  = "<<common*rest<<endl;
		cout << "       c    = "<<common<<endl;
		cout << "       r    = "<<rest<<endl;
	}

	if (dr!=1)
	{
		if (is_a<add>(rest))
			s << " ( "<<rest<<" ) \\frac{"<<common<<"}{"<<dr<<"}";
		else
		{
			if (rest!=1)
				s << "\\frac{"<<common*rest<<"}{"<<dr<<"}";
			else
				s << "\\frac{"<<common<<"}{"<<dr<<"}";
		}
	} else
	{
		if (is_a<add>(rest))
			s << common << "( "<<rest<<" )";
		else
		{
			if (rest!=1)
				s << " ( " <<rest*common << " ) "; // negative case
			else
				s <<  common;
		}
	}

	string str1 = s.str();
	str1 = ReplaceString( str1, "(t,x,y,z)", "");
	str1 = ReplaceString( str1, "(t,\\x,\\y,\\z)", "");
	return str1;
}


string BASE::factorize_latex_v1(ex co)
{
// dev:
	ostringstream s;
	s << latex;
	// prepare co
	ex co1 = subs( co, subs_vels );
	ex r = GiNaC::normal( co1 );
	ex nr = numer(r);
	nr = GiNaC::subs( nr, subs_vels);
	nr = GiNaC::normal( nr );
	ex nrex = GiNaC::expand( nr , factor_options::all );
	ex dr = denom(r);
	nr=GiNaC::expand(nr);
	exmap mp;
	ex p = nrex.to_polynomial(mp);
	nr=GiNaC::subs(GiNaC::factor(p),mp);
	ex common=(ex)1;
	ex rest=(ex)1;
	bool negative = false;
	{
		for (int i=0;i<nr.nops();i++)
		{
			if (!is_a<add>(nr.op(i)))
			{
				common *= nr.op(i);
				if (is_a<numeric>(nr.op(i)))
				if (nr.op(i)<0)  negative=true;
			}
		}
		rest = GiNaC::expand(GiNaC::normal(nr/common));
	}

	if (negative) { common = -common; rest=-rest; }

	if (dr!=1)
	{
		if (is_a<add>(rest))
			s << " ( "<<rest<<" ) \\frac{"<<common<<"}{"<<dr<<"}";
		else
		{
			if (rest!=1)
				s << "\\frac{"<<common*rest<<"}{"<<dr<<"}";
			else
				s << "\\frac{"<<common<<"}{"<<dr<<"}";
		}
	} else
	{
		if (is_a<add>(rest))
			s << common << "( "<<rest<<" )";
		else
		{
			if (rest!=1)
				s << " ( " <<rest*common << " ) "; // negative case
			else
				s <<  common;
		}
	}

	string str1 = s.str();
	str1 = ReplaceString( str1, "(t,x,y,z)", "");
	str1 = ReplaceString( str1, "(t,\\x,\\y,\\z)", "");
	return str1;
}

void BASE::factorize_latex(ostringstream &sout1, ostringstream &sout2, ex co, int &counter, const char* id)
{
	const int max_ps=10;
	ostringstream s,s2;
	s << latex;
	s2 << latex;
	// prepare co
	ex co1 = subs( co, subs_vels );
	ex r = GiNaC::normal( co1 );
	ex nr = GiNaC::expand(numer(r));
	ex dr = denom(r);
	// decomposition
	ex common=(ex)1;
	ex rest=(ex)1;
	bool negative = false;

	if (is_a<add>(nr))
	{
		// convert to poly
		exmap mp;
		ex p = nr.to_polynomial(mp);
		common = (ex)1;
		for (int i=1;i<p.nops();i++)
		{
			ex c1 = gcd(p.op(0), p.op(i));
			if (i==1)
				common = c1;
			else
				common = gcd(common, c1);
//			common = (common==1) ? c1 : gcd(common, c1);
		}
		common = GiNaC::subs(common, mp);
		rest = GiNaC::expand(nr/common);
		negative = sign(common);
	} else
	{
		// determine sign of an expression
		negative = sign(nr);
		common=nr;
		rest=(ex)1;
	}

	if (negative) { common = -common; rest=-rest; }

	// test
	ex test = GiNaC::expand(common*rest - nr);
	if (test!=0)
	{
		cout << "error: test = "<<test<<endl;
		cout << "       nr   = "<<nr<<endl;
		cout << "       c*r  = "<<common*rest<<endl;
		cout << "       c    = "<<common<<endl;
		cout << "       r    = "<<rest<<endl;
	}

	if (dr!=1)
	{
		if (is_a<add>(rest))
		{
			if (rest.nops()>max_ps)
			{
				counter++;
//				s << " { \\color{darkgreen} \\label{E"<<id<<counter<<"}\\hyperref[C"<<id<<counter<<"]{C_{ "<<counter<<"}}}";
				s << " \\phantomsection\\label{E"<<id<<counter<<"}\n";
				s << " ${ \\color{darkgreen}\\hyperref[C"<<id<<counter<<"]{C_{ "<<counter<<"}}}";
				s << " {\\frac{"<<common<<"}{"<<dr<<"}}";
				s2 << "\\phantomsection\\label{C"<<id<<counter<<"}\n";
				s2 << "${ \\color{darkgreen} \\hyperref[E"<<id<<counter<<"]{C_{ "<<(counter)<<"}} } = " << rest << "$\n\\vskip 1em \n";
			} else
			{
				s << "$ ( "<<rest<<" ) \\frac{"<<common<<"}{"<<dr<<"}";
			}
		}
		else
		{
			if (rest!=1)
				s << "$\\frac{"<<common*rest<<"}{"<<dr<<"}";
			else
				s << "$\\frac{"<<common<<"}{"<<dr<<"}";
		}
	} else
	{
		if (is_a<add>(rest))
		{
			if (rest.nops()>max_ps)
			{
				counter++;
//				s << common << "{ \\color{darkgreen} \\label{E"<<id<<counter<<"}\\hyperref[C"<<id<<counter<<"]{C_{ "<<counter<<"} } }";
				s << "\\label{E"<<id<<counter<<"}\n";
				s << "$" << common << "{ \\color{darkgreen} \\hyperref[C"<<id<<counter<<"]{C_{ "<<counter<<"} } }";
				s2 << "\\phantomsection\\label{C"<<id<<counter<<"}\n";
				s2 << "${ \\color{darkgreen} \\hyperref[E"<<id<<counter<<"]{C_{ "<<(counter)<<"}} } = " << rest << "$ \n \\vskip 1em \n";
			} else
			{
				s << "$" << common << "( "<<rest<<" )";
			}
		}
		else
		{
			if (rest!=1)
				s << "$ ( " <<rest*common << " ) "; // negative case
			else
				s << "$" << common;
		}
	}

	string str1 = s.str();
	str1 = ReplaceString( str1, "(t,x,y,z)", "");
	str1 = ReplaceString( str1, "(t,\\x,\\y,\\z)", "");
	sout1 << str1;
	string str2 = s2.str();
	str2 = ReplaceString( str2, "(t,x,y,z)", "");
	str2 = ReplaceString( str2, "(t,\\x,\\y,\\z)", "");
	sout2 << str2;
}

void BASE::factorize_latex_simple(ostringstream &sout1, ex co)
{
//	const int max_ps=10;
	ostringstream s,s2;
	s << latex;
//	s2 << latex;
	// prepare co
	ex co1 = subs( co, subs_vels );
	ex r = GiNaC::normal( co1 );
	ex nr = GiNaC::expand(numer(r));
	ex dr = denom(r);
	// decomposition
	ex common=(ex)1;
	ex rest=(ex)1;
	bool negative = false;

	if (is_a<add>(nr))
	{
		// convert to poly
		exmap mp;
		ex p = nr.to_polynomial(mp);
//		cout << "polynomial = "<<p<<"\n\n";
		common = (ex)1;
		for (int i=1;i<p.nops();i++)
		{
//			cout << "i="<<i<<"\n";
			ex c1 = gcd(p.op(0), p.op(i));
//			cout << "comparing " << p.op(0) << " and "<< p.op(i) << endl;
//			cout << "c1 = " << c1 << "\n\n";
			if (i==1)
				common = c1;
			else
				common = gcd(common, c1);
//			cout << " common = "<<common << "\n\n";
		}
		common = GiNaC::subs(common, mp);
//		cout << "common = "<<common << "\n\n";
		rest = GiNaC::expand(nr/common);
		negative = sign(common);
	} else
	{
		// determine sign of an expression
		negative = sign(nr);
		common=nr;
		rest=(ex)1;
	}
	if (negative) { common = -common; rest=-rest; }

	// test
	ex test = GiNaC::expand(common*rest - nr);
	if (test!=0)
	{
		cout << "debug: co   = "<<co<<endl;
		cout << "       test = "<<test<<endl;
		cout << "       nr   = "<<nr<<endl;
		cout << "       dr   = "<<dr<<endl;
		cout << "       c*r  = "<<common*rest<<endl;
		cout << "       c    = "<<common<<endl;
		cout << "       r    = "<<rest<<endl;
	}

	if (dr!=1)
	{
		if (is_a<add>(rest))
		{
//			if (rest.nops()>max_ps)
//			{
//				s << " { \\color{darkgreen} \\gamma_{ "<<(++counter)<<"}} \\frac{"<<common<<"}{"<<dr<<"}";
//				s2 << "${ \\color{darkgreen} \\gamma_{ "<<(counter)<<"} } = " << rest << "$"<< endl << "\\vskip 1em" << endl;
//			} else
			{
				s << " ( "<<rest<<" ) \\frac{"<<common<<"}{"<<dr<<"}";
			}
		}
		else
		{
			if (rest!=1)
				s << "\\frac{"<<common*rest<<"}{"<<dr<<"}";
			else
				s << "\\frac{"<<common<<"}{"<<dr<<"}";
		}
	} else
	{
		if (is_a<add>(rest))
		{
//			if (rest.nops()>max_ps)
//			{
//				s << common << "{ \\color{darkgreen} \\gamma_{ "<<(counter++)<<"} }";
//				s2 << "${ \\color{darkgreen} \\gamma_{ "<<(counter++)<<"} } = " << rest << "$"<< endl << "\\vskip 1em" << endl;
//			} else
			{
				if (common!=1)
					s << common << "( "<<rest<<" )";
				else
					s << "( "<<rest<<" )";
			}
		}
		else
		{
			if (rest!=1)
				s << " ( " <<rest*common << " ) "; // negative case
			else
				s <<  common;
		}
	}

	string str1 = s.str();
	str1 = ReplaceString( str1, "(t,x,y,z)", "");
	str1 = ReplaceString( str1, "(t,\\x,\\y,\\z)", "");
//	sout1 << "{r_b} " << str1 << "{r_e}";
	sout1 <<  str1;
//	string str2 = s2.str();
//	str2 = ReplaceString( str2, "(t,x,y,z)", "");
//	sout2 << str2;
}


void BASE::replace_coefs_expression_recursive(ex& expr, int &sym_index, vct &bas2, lst  &sym_subs, const char* prefix, int level, ex &new_expr, ex term)
{
	int n = bas2.size();
	for (int i=level;i<n;i++)
	{
		for (int d=expr.ldegree(bas2[i]);d<=expr.degree(bas2[i]);d++)
		if (d!=0)
		{
			ex c = expr.coeff(bas2[i], d);
			if (c!=0)
			{
				ex new_term=pow( bas2[i], d);
				expr = expand( expr - c*new_term );
				replace_coefs_expression_recursive( c, sym_index, bas2, sym_subs, prefix, level+1, new_expr, term*new_term);
			}
		}
	}
	// now we should be left with the real coefficient
	if (expr != 0)
	{
		if (expr.nops() > 1) // more than one subexpression is there
		{
//				cout << "at level " << level <<", we found this:"<<endl;
//				cout << "(" << expr <<") times term " << endl;//(" << term << ")"<<endl;
			// create new symbol
			char str[200];
			sprintf(str, "%s%d",prefix,sym_index++);
			symbol S=symbol(str);
			sym_subs.append( S == expr );
			new_expr += S*term;
		} else
			new_expr += expr*term;

	}

}

// general tools
// replace all coeffs in expr by symbolic polynomial based on bas2
// debug / TODO: for now, assuming quadratic polynomials only?
void BASE::replace_coefs_expression(ex& expr, int &sym_index, vct &bas2, lst  &sym_subs, const char* prefix= "C")
{
	ex e = GiNaC::expand( expr );
	ex new_expr=0;

	ex term = (ex)1;
	replace_coefs_expression_recursive( e, sym_index, bas2, sym_subs, prefix, 0, new_expr, term);
//		cout << "old expr:"<<endl<<expr<<endl;
//		cout << "new expr:"<<endl<<new_expr<<endl;
//		cout << "diff:"<<endl<<expand(expr-new_expr)<<endl;
	expr = new_expr;
}

void BASE::dump_lst(const char *dir_name, const char *subdir_name, const char*filename_prefix, int counter, lst& list, bool erase=false)
{
	char fname[500];
	mkdir(dir_name,0777);
	sprintf(fname,"%s/%s",dir_name,subdir_name);
	mkdir(fname,0777);
	sprintf(fname,"%s/%s/%s_%d",dir_name,subdir_name,filename_prefix,counter);
	stringstream sfn;
	ofstream fout(fname);
	for (lst::const_iterator i = list.begin(); i != list.end(); ++i)
	{
		fout << *i << endl;
	}    
	fout.close();
	list.remove_all();
}

void BASE::dump_lst(const char *dir_name, const char*filename_prefix, int counter, lst& list, bool erase=false)
{
	mkdir(dir_name,0777);
	char fname[500];
	sprintf(fname,"%s/%s_%d",dir_name,filename_prefix,counter);
	stringstream sfn;
	ofstream fout(fname);
	for (lst::const_iterator i = list.begin(); i != list.end(); ++i)
	{
		fout << *i << endl;
	}    
	fout.close();
	list.remove_all();
}

ex BASE::load_ex(const std::string &filename, parser &reader)
{
	ifstream gin(filename);
	if (!gin.is_open()) cerr << "Error: " << strerror(errno);
	std::string line;
	getline( gin, line );
	ex c=(line.length()>0) ? reader(line) : 0;
	//cout << "line read: "<<c<<endl;
	gin.close();
	return c;
}
