/*

	This file is part of PEST++.

	PEST++ is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	PEST++ is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with PEST++.  If not, see<http://www.gnu.org/licenses/>.
*/
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include <math.h>
#include <sstream>
#include <Eigen/Dense>
#include <cassert>
#include <iostream>
#include "Transformation.h"
#include "Transformable.h"
#include "SVD_PROPACK.h"
#include "eigen_tools.h"
#include "Jacobian.h"
#include "QSqrtMatrix.h"
#include "pest_data_structs.h"
#include "debug.h"
#include "Regularization.h"
#include "Serialization.h"
#include "eigen_tools.h"

using namespace std;
using namespace Eigen;

///////////////// Transformation Methods /////////////////

///////////////// TranMapBase Methods /////////////////
void TranMapBase::insert(const string &item_name, double item_value)
{
	items[item_name] = item_value;
}

void TranMapBase::insert(const Parameters &pars)
{
	for (const auto &ipar : pars)
	{
		items[ipar.first] = ipar.second;
	}
}

void TranMapBase::reset(const Parameters &pars)
{
	items.clear();
	for (const auto &ipar : pars)
	{
		items[ipar.first] = ipar.second;
	}
}

void TranMapBase::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranMapBase)" << endl;
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << (*b).first << ";  value = " << (*b).second << endl;
	}
}

pair<bool, double> TranMapBase::get_value(const string &name) const
{
	pair<bool, double> ret_val(false, 0.0);
	map<string, double>::const_iterator it;

	it = items.find(name);
	if (it !=items.end()) {
		ret_val = pair<bool, double>(true, (*it).second);
	}
	return ret_val;
}

///////////////// TranSetBase Methods /////////////////
void TranSetBase::insert(const string &item_name)
{
	items.insert(item_name);
}

void TranSetBase::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranSetBase)" << endl;
	for (set<string>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << *b << endl;
	}
}

bool TranSetBase::has_value(const string &name) const
{
	bool ret_val = false;
	set<string>::const_iterator it;

	it = items.find(name);
	if (it !=items.end()) {
		ret_val = true;
	}
	return ret_val;
}

///////////////// TranOffset Methods /////////////////
void TranOffset::forward(Transformable &data)
{
	Transformable::iterator data_iter, data_end = data.end();
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
		data_iter = data.find(b->first);
		if (data_iter != data_end)
		{
			(*data_iter).second += (*b).second;
		}
	}
}

void TranOffset::reverse(Transformable &data)
{
	Transformable::iterator data_iter, data_end = data.end();
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
		data_iter = data.find(b->first);
		if (data_iter != data_end)
		{
			(*data_iter).second -= b->second;
		}
	}
}

void TranOffset::jacobian_forward(Jacobian &jac)
{
	Transformable &data = jac.base_numeric_parameters;
	forward(data);
}

void TranOffset::jacobian_reverse(Jacobian &jac)
{
	Transformable &data = jac.base_numeric_parameters;
	reverse(data);
}

void TranOffset::d2_to_d1(Transformable &del_data, Transformable &data)
{
	// Offset transformation does not affect derivatives.
	// Nothing to do.
	reverse(data);
}

void TranOffset::d1_to_d2(Transformable &del_data, Transformable &data)
{
	// Offset transformation does not affect derivatives.
	// Nothing to do.
	forward(data);
}

void TranOffset::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranOffset)" << endl;
	for (map<string, double>::const_iterator b = items.begin(), e = items.end();
		b != e; ++b) {
		os << "  item name = " << (*b).first << ";  offset value = " << (*b).second << endl;
	}
}

///////////////// TranScale Methods /////////////////
void TranScale::forward(Transformable &data)
{
	Transformable::iterator data_iter, data_end = data.end();
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
		data_iter = data.find(b->first);
		if (data_iter != data_end)
		{
			(*data_iter).second *= b->second;
		}
	}
}

void TranScale::reverse(Transformable &data)
{
	Transformable::iterator data_iter, data_end = data.end();
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
		data_iter = data.find(b->first);
		if (data_iter != data_end)
		{
			(*data_iter).second /= b->second;
		}
	}
}

void TranScale::jacobian_forward(Jacobian &jac)
{
	size_t icol = 0;
	double factor = 0;
	Transformable &data = jac.base_numeric_parameters;
	unordered_map<string, int> par_2_col_map = jac.get_par2col_map();
	auto iter_end = par_2_col_map.end();
	for (const auto &irec : items)
	{
		auto iter = par_2_col_map.find(irec.first);
		if (iter != iter_end)
		{
			icol = iter->second;
			factor = irec.second;
			jac.matrix.col(icol) /= factor;
		}
	}
	forward(data);
}

void TranScale::jacobian_reverse(Jacobian &jac)
{
	size_t icol = 0;
	double factor = 0;
	Transformable &data = jac.base_numeric_parameters;
	unordered_map<string, int> par_2_col_map = jac.get_par2col_map();
	auto iter_end = par_2_col_map.end();
	for (const auto &irec : items)
	{
		auto iter = par_2_col_map.find(irec.first);
		if (iter != iter_end)
		{
			icol = iter->second;
			factor = irec.second;
			jac.matrix.col(icol) *= factor;
		}
	}
	reverse(data);
}

void TranScale::d1_to_d2(Transformable &del_data, Transformable &data)
{
	Transformable::iterator del_data_iter, del_data_end = del_data.end();
	for (map<string, double>::const_iterator b = items.begin(), e = items.end();
		b != e; ++b) {
		del_data_iter = del_data.find(b->first);
		if (del_data_iter != del_data_end)
		{
			(*del_data_iter).second /= b->second;
		}
	}
	forward(data);
}

void TranScale::d2_to_d1(Transformable &del_data, Transformable &data)
{
	Transformable::iterator del_data_iter, del_data_end = del_data.end();
	for (map<string, double>::const_iterator b = items.begin(), e = items.end();
		b != e; ++b) {
		del_data_iter = del_data.find(b->first);
		if (del_data_iter != del_data_end)
		{
			(*del_data_iter).second *= b->second;
		}
	}
	reverse(data);
}

void TranScale::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranScale)" << endl;
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << (*b).first << ";  scale value = " << (*b).second << endl;
	}
}

///////////////// TranLog10 Methods /////////////////
void TranLog10::forward(Transformable &data)
{
	Transformable::iterator data_iter, data_end = data.end();
	for (set<string>::const_iterator b=items.begin(), e=items.end(); b!=e; ++b)
	{
		data_iter = data.find(*b);
		if (data_iter != data_end)
		{
			(*data_iter).second = log10((*data_iter).second);
		}
	}
}

void TranLog10::reverse(Transformable &data)
{
	Transformable::iterator data_iter, data_end = data.end();
	for (set<string>::const_iterator b=items.begin(), e=items.end(); b!=e; ++b)
	{
		data_iter = data.find(*b);
		if (data_iter != data_end)
		{
			(*data_iter).second = pow(10.0, (*data_iter).second);
		}
	}
}

void TranLog10::jacobian_forward(Jacobian &jac)
{
	size_t icol = 0;
	double factor = 0;
	double d = 0;
	Transformable &data = jac.base_numeric_parameters;
	forward(data);
	unordered_map<string, int> par_2_col_map = jac.get_par2col_map();
	auto iter_end = par_2_col_map.end();
	for (const auto &ipar : items)
	{
		auto iter = par_2_col_map.find(ipar);
		if (iter != iter_end)
		{
			d = data.get_rec(ipar);
			icol = iter->second;
			factor = pow(10, d) * log(10.0);
		}
		jac.matrix.col(icol) *= factor;
	}
}

void TranLog10::jacobian_reverse(Jacobian &jac)
{
	size_t icol = 0;
	double factor = 0;
	double d = 0;
	Transformable &data = jac.base_numeric_parameters;
	reverse(data);
	unordered_map<string, int> par_2_col_map = jac.get_par2col_map();
	auto iter_end = par_2_col_map.end();
	for (const auto &ipar : items)
	{
		auto iter = par_2_col_map.find(ipar);
		if (iter != iter_end)
		{
			d = data.get_rec(ipar);
			icol = iter->second;
			factor = 1.0 / (d * log(10.0));
			jac.matrix.col(icol) *= factor;
		}
	}
}

void TranLog10::d1_to_d2(Transformable &del_data, Transformable &data)
{
	forward(data);
	Transformable::iterator del_data_iter, del_data_end = del_data.end();
	for (set<string>::const_iterator b = items.begin(), e = items.end(); b != e; ++b)
	{
		del_data_iter = del_data.find(*b);
		if (del_data_iter != del_data_end)
		{
			double d1 = data.get_rec(*b);
			double factor = pow(10.0, d1) * log(10.0);
			(*del_data_iter).second *= factor;
		}
	}
}

void TranLog10::d2_to_d1(Transformable &del_data, Transformable &data)
{
	reverse(data);
	Transformable::iterator del_data_iter, del_data_end = del_data.end();
	for (set<string>::const_iterator b = items.begin(), e = items.end(); b != e; ++b)
	{
		del_data_iter = del_data.find(*b);
		if (del_data_iter != del_data_end)
		{
			double d2 = data.get_rec(*b);
			double factor = 1.0 / (d2 * log(10.0));
			(*del_data_iter).second *= factor;
		}
	}
	reverse(data);
}

void TranLog10::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranLog10)" << endl;
	for (set<string>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << *b << endl;
	}
}

///////////////// TranFixed Methods /////////////////
void TranFixed::forward(Transformable &data)
{
	for (map<string,double>::iterator b=items.begin(), e=items.end(); b!=e; ++b)
	{
		data.erase(b->first);
	}
}

void TranFixed::reverse(Transformable &data)
{
	for (map<string,double>::iterator b=items.begin(), e=items.end(); b!=e; ++b)
	{
		data.insert(b->first, b->second);
	}
}

void TranFixed::jacobian_forward(Jacobian &jac)
{
	Transformable &data = jac.base_numeric_parameters;
	set<string> rm_par_set;
	for (const auto &irec : items)
	{
		rm_par_set.insert(irec.first);
	}
	//remove Fixed parameters from base_parameter_names
	jac.remove_cols(rm_par_set);
	forward(data);
}

void TranFixed::jacobian_reverse(Jacobian &jac)
{
	Transformable &data = jac.base_numeric_parameters;
	set<string> new_pars;
	for (auto &i : items)
	{
		new_pars.insert(i.first);
	}
	jac.add_cols(new_pars);
	reverse(data);
}

void TranFixed::d1_to_d2(Transformable &del_data, Transformable &data)
{
	for (map<string, double>::iterator b = items.begin(), e = items.end(); b != e; ++b)
	{
		del_data.erase(b->first);
	}
	forward(data);
}

void TranFixed::d2_to_d1(Transformable &del_data, Transformable &data)
{
	for (map<string, double>::iterator b = items.begin(), e = items.end(); b != e; ++b)
	{
		del_data.insert(b->first, 0.0);
	}
	reverse(data);
}

void TranFixed::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranFixed)" << endl;
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << (*b).first << ";  imposed value = " << (*b).second << endl;
	}
}

void TranFrozen::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranFrozen)" << endl;
	for (map<string,double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << (*b).first << ";  imposed value = " << (*b).second << endl;
	}
}

void TranTied::insert(const string &item_name, const pair<string, double> &item_value)
{
	items[item_name] = item_value;
}

void TranTied::forward(Transformable &data)
{
	for (map<string, pair_string_double>::iterator ii = items.begin(); ii != items.end(); ++ii)
	{
		data.erase(ii->first);
	}
}

void TranTied::reverse(Transformable &data)
{
	string const *base_name;
	double *factor;
	Transformable::iterator base_iter;
	for (map<string, pair_string_double>::iterator b = items.begin(), e = items.end();
		b != e; ++b)
	{
		base_name = &(b->second.first);
		factor = &(b->second.second);
		base_iter = data.find(*base_name);
		if (base_iter != data.end())
		{
			//cout << b->first << ',' << data[b->first] <<  ',' << (*base_iter).second << endl;
			data.erase(b->first);
			data.insert(b->first, (*base_iter).second * (*factor));
			//cout << b->first << ',' << data[b->first] << ',' << (*base_iter).second << endl;
		}

	}
}

void TranTied::jacobian_forward(Jacobian &jac)
{
	throw(PestError("Error: TranTied::jacobian_forward - TranTied does not support Jacobian transformations"));
}

void TranTied::jacobian_reverse(Jacobian &jac)
{
	throw(PestError("Error: TranTied::jacobian_forward - TranTied does not support Jacobian transformations"));
}

void TranTied::d1_to_d2(Transformable &del_data, Transformable &data)
{
	throw(PestError("Error: TranTied::d1_to_d2 - TranTied does not support d1_to_d2 transformations"));
}

void TranTied::d2_to_d1(Transformable &del_data, Transformable &data)
{
	throw(PestError("Error: TranTied::d2_to_d1 - TranTied does not support d2_to_d1 transformations"));
}

void TranTied::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranTied)" << endl;
	for (map<string, pair_string_double>::const_iterator b=items.begin(), e=items.end();
		b!=e; ++b) {
			os << "  item name = " << (*b).first << "   tied to \"" << (*b).second.first <<
				"\" with factor " <<  (*b).second.second<<  endl;
	}
}

const  Eigen::SparseMatrix<double>& TranSVD::get_vt() const
{
	return Vt;
}

TranSVD::TranSVD(int _max_sing, double _eign_thresh, const string &_name) : Transformation(_name)
{
	tran_svd_pack = new SVD_EIGEN(_max_sing, _eign_thresh);
}

TranSVD::TranSVD(const TranSVD &rhs)
	: Transformation(rhs), base_parameter_names(rhs.base_parameter_names),
	super_parameter_names(rhs.super_parameter_names),
	obs_names(rhs.obs_names),
	SqrtQ_J(rhs.SqrtQ_J),
	Sigma(rhs.Sigma),
	U(rhs.U),
	Vt(rhs.Vt),
	init_base_numeric_parameters(rhs.init_base_numeric_parameters),
	frozen_derivative_parameters(rhs.frozen_derivative_parameters)
{
	tran_svd_pack = rhs.tran_svd_pack->clone();
}

void TranSVD::set_SVD_pack_propack()
{
	int max_sing = tran_svd_pack->get_max_sing();
	double eigthresh = tran_svd_pack->get_eign_thres();
	delete tran_svd_pack;
	tran_svd_pack = new SVD_PROPACK(max_sing, eigthresh);
}

void TranSVD::set_performance_log(PerformanceLog *performance_log)
{
	tran_svd_pack->set_performance_log(performance_log);
}

void TranSVD::calc_svd()
{
	debug_msg("TranSVD::calc_svd begin");
	stringstream sup_name;
	VectorXd Sigma_trunc;
	tran_svd_pack->solve_ip(SqrtQ_J, Sigma, U, Vt, Sigma_trunc);
	// calculate the number of singluar values above the threshold

	debug_print(Sigma);
	debug_print(U);
	debug_print(Vt);
	debug_print(Sigma_trunc);

	int n_sing_val = Sigma.size();

	super_parameter_names.clear();
	for(int i=0; i<n_sing_val; ++i) {
		sup_name.str("");
		sup_name << "SUP_";
		sup_name << i+1;
		super_parameter_names.push_back(sup_name.str());
	}
	if (n_sing_val <= 0 )
	{
		throw PestError("TranSVD::update() - super parameter transformation returned 0 super parameters.  Jacobian must equal 0.");
	}
	debug_print(super_parameter_names);
	debug_msg("TranSVD::calc_svd end");
}

void TranSVD::update_reset_frozen_pars(const Jacobian &jacobian, const QSqrtMatrix &Q_sqrt, const Parameters &base_numeric_pars,
		int maxsing, double _eigthresh, const vector<string> &par_names, const vector<string> &_obs_names,
		const Parameters &_frozen_derivative_pars)
{
	debug_msg("TranSVD::update_reset_frozen_pars begin");
	debug_print(_frozen_derivative_pars);
	stringstream sup_name;
	super_parameter_names.clear();

	tran_svd_pack->set_max_sing(maxsing);
	tran_svd_pack->set_eign_thres(_eigthresh);
	obs_names = _obs_names;

	//these are where the derivative was computed so they can be different than the frozen values;
	init_base_numeric_parameters = base_numeric_pars;
	base_parameter_names = par_names;
	//remove frozen parameters from base_parameter_names
	auto end_iter = std::remove_if(base_parameter_names.begin(), base_parameter_names.end(),
		[&_frozen_derivative_pars](string &str)->bool{return _frozen_derivative_pars.find(str)!=_frozen_derivative_pars.end();});
	base_parameter_names.resize(std::distance(base_parameter_names.begin(), end_iter));
	frozen_derivative_parameters = _frozen_derivative_pars;
	//remove frozen derivatives from matrix parameter list
	std::remove_if(base_parameter_names.begin(), base_parameter_names.end(),
		[this](string &str)->bool{return this->frozen_derivative_parameters.find(str)!=this->frozen_derivative_parameters.end();});

	SqrtQ_J = Q_sqrt.get_sparse_matrix(obs_names, DynamicRegularization::get_unit_reg_instance()) * jacobian.get_matrix(obs_names, base_parameter_names);

	calc_svd();
	debug_print(this->base_parameter_names);
	debug_print(this->frozen_derivative_parameters);
	debug_msg("TranSVD::update_reset_frozen_pars end");
}

void TranSVD::update_add_frozen_pars(const Parameters &frozen_pars)
{
	debug_msg("TranSVD::update_reset_frozen_pars begin");
	debug_print(frozen_pars);
	vector<size_t> del_col_ids;
	Parameters new_frozen_pars;
	for (auto &ipar : frozen_pars)
	{
		auto iter = frozen_derivative_parameters.find(ipar.first);
		if (iter == frozen_derivative_parameters.end())
		{
			new_frozen_pars.insert(ipar);
		}
	}

	frozen_derivative_parameters.insert(new_frozen_pars);

	// build list of columns that needs to be removed from the matrix
	for (int i=0; i<base_parameter_names.size(); ++i)
	{
		if(new_frozen_pars.find(base_parameter_names[i]) != new_frozen_pars.end())
		{
			del_col_ids.push_back(i);
		}
	}

	if (del_col_ids.size() == base_parameter_names.size())
	{
		throw PestError("TranSVD::update_add_frozen_pars - All parameters are frozen in SVD transformation");
	}
	//remove frozen parameters from base_parameter_names
	auto end_iter = std::remove_if(base_parameter_names.begin(), base_parameter_names.end(),
		[&new_frozen_pars](string &str)->bool{return new_frozen_pars.find(str)!=new_frozen_pars.end();});
	base_parameter_names.resize(std::distance(base_parameter_names.begin(), end_iter));
	matrix_del_cols(SqrtQ_J, del_col_ids);
	calc_svd();
	debug_print(this->base_parameter_names);
	debug_print(this->frozen_derivative_parameters);
	debug_msg("TranSVD::update_reset_frozen_pars end");
}
void TranSVD::reverse(Transformable &data)
{
	// Transform super-parameters to base parameters
	assert(Vt.cols() == base_parameter_names.size());
	int n_base = Vt.cols();
	vector<double> super_par_vec = data.get_data_vec(super_parameter_names);
	vector<double>::iterator it;
	for (it=super_par_vec.begin(); it!=super_par_vec.end(); ++it)
	{
		(*it) -= 10.0;
	}
	Transformable ret_base_pars;
	int n_sing_val = Sigma.size();
	VectorXd delta_base_mat = Vt.block(0,0,n_sing_val, Vt.cols()).transpose() *  stlvec_2_egienvec(super_par_vec);
	for (int i=0; i<n_base; ++i) {
		ret_base_pars.insert(base_parameter_names[i], delta_base_mat(i) + init_base_numeric_parameters.get_rec(base_parameter_names[i]));
	}

	data = ret_base_pars;
}

void TranSVD::forward(Transformable &data)
{
	//Transform base parameters to super-parameters
	Transformable super_pars;
	VectorXd value;

	Transformable delta_data = init_base_numeric_parameters;
	delta_data *= 0.0;

	for (auto &it : data)
	{
		delta_data[it.first] = it.second - init_base_numeric_parameters.get_rec(it.first);
	}
	int n_sing_val = Sigma.size();
	value = Vt * delta_data.get_data_eigen_vec(base_parameter_names);
	for (int i=0; i<n_sing_val; ++i) {
		super_pars.insert(super_parameter_names[i], value(i)+10.0);
	}
	data = super_pars;
}

void TranSVD::jacobian_forward(Jacobian &jac)
{
	Transformable &data = jac.base_numeric_parameters;
	Eigen::SparseMatrix<double> old_matrix = jac.get_matrix(jac.observation_list(), base_parameter_names);
	Eigen::SparseMatrix<double> super_jacobian;
	super_jacobian = old_matrix * Vt.transpose();
	jac.matrix = super_jacobian;
	jac.base_numeric_par_names = super_parameter_names;

	forward(data);
}

void TranSVD::jacobian_reverse(Jacobian &jac)
{
	Transformable &data = jac.base_numeric_parameters;
	Eigen::SparseMatrix<double> old_matrix = jac.get_matrix(jac.observation_list(), super_parameter_names);
	Eigen::SparseMatrix<double> base_jacobian;
	base_jacobian = old_matrix * Vt;
	jac.matrix = base_jacobian;
	jac.base_numeric_par_names = base_parameter_names;
	reverse(data);
}

void TranSVD::d1_to_d2(Transformable &del_data, Transformable &data)
{
	Transformable new_data;
	VectorXd d1_vec = del_data.get_partial_data_eigen_vec(base_parameter_names);
	VectorXd d2_vec = Vt * d1_vec;
	for (int i = 0; i<Sigma.size(); ++i) {
		new_data[super_parameter_names[i]] = d2_vec[i];
	}
	del_data = new_data;
	forward(data);
}

void TranSVD::d2_to_d1(Transformable &del_data, Transformable &data)
{
	Transformable new_data;
	VectorXd d2_vec = del_data.get_partial_data_eigen_vec(super_parameter_names);
	VectorXd d1_vec = Vt.transpose() * d2_vec;
	for (size_t i = 0; i < base_parameter_names.size(); ++i)
	{
		new_data[base_parameter_names[i]] = d1_vec[i];
	}
	del_data = new_data;
	reverse(data);
}

void TranSVD::save(ostream &fout) const
{
	size_t size;
	vector<int8_t> serial_data;
	serial_data = Serialization::serialize(base_parameter_names);
	size = serial_data.size();
	fout.write((char*)&size, sizeof(size));
	fout.write((char*)serial_data.data(), size);

	serial_data = Serialization::serialize(super_parameter_names);
	size = serial_data.size();
	fout.write((char*)&size, sizeof(size));
	fout.write((char*)serial_data.data(), size);

	serial_data = Serialization::serialize(obs_names);
	size = serial_data.size();
	fout.write((char*)&size, sizeof(size));
	fout.write((char*)serial_data.data(), size);

	save_triplets_bin(SqrtQ_J, fout);
	save_vector_bin(Sigma, fout);
	save_triplets_bin(U, fout);
	save_triplets_bin(Vt, fout);

	serial_data = Serialization::serialize(init_base_numeric_parameters);
	size = serial_data.size();
	fout.write((char*)&size, sizeof(size));
	fout.write((char*)serial_data.data(), size);

	serial_data = Serialization::serialize(frozen_derivative_parameters);
	size = serial_data.size();
	fout.write((char*)&size, sizeof(size));
	fout.write((char*)serial_data.data(), size);
}

void TranSVD::read(istream &fin)
{
	size_t size;
	vector<int8_t> serial_data;

	base_parameter_names.clear();
	fin.read((char*)&size, sizeof(size));
	serial_data.clear();
	serial_data.resize(size);
	fin.read((char*)serial_data.data(), size);
	Serialization::unserialize(serial_data, base_parameter_names);

	super_parameter_names.clear();
	fin.read((char*)&size, sizeof(size));
	serial_data.clear();
	serial_data.resize(size);
	fin.read((char*)serial_data.data(), size);
	Serialization::unserialize(serial_data, super_parameter_names);

	obs_names.clear();
	fin.read((char*)&size, sizeof(size));
	serial_data.clear();
	serial_data.resize(size);
	fin.read((char*)serial_data.data(), size);
	Serialization::unserialize(serial_data, obs_names);

	load_triplets_bin(SqrtQ_J, fin);
	load_vector_bin(Sigma, fin);
	load_triplets_bin(U, fin);
	load_triplets_bin(Vt, fin);

	init_base_numeric_parameters.clear();
	fin.read((char*)&size, sizeof(size));
	serial_data.clear();
	serial_data.resize(size);
	fin.read((char*)serial_data.data(), size);
	Serialization::unserialize(serial_data, init_base_numeric_parameters);

	frozen_derivative_parameters.clear();
	fin.read((char*)&size, sizeof(size));
	serial_data.clear();
	serial_data.resize(size);
	fin.read((char*)serial_data.data(), size);
	Serialization::unserialize(serial_data, frozen_derivative_parameters);
}

ParameterGroupInfo TranSVD::build_par_group_info(const ParameterGroupInfo &base_pg_info)
{
	double derinc_sup;
	double derinc_par;
	int max_col;
	double max_val;
	ParameterGroupInfo pg_info;
	stringstream grp_name;
	for (int i_sup=0, n_sup=super_parameter_names.size(); i_sup < n_sup; ++i_sup)
	{
		get_MatrixXd_row_abs_max(Vt, i_sup, &max_col, &max_val);
		derinc_par = base_pg_info.get_group_rec_ptr(base_parameter_names[max_col])->derinc;
		derinc_sup = .01;
		grp_name.str("");
		grp_name << "g_" << super_parameter_names[i_sup];
		ParameterGroupRec sup_rec(grp_name.str(), "ABSOLUTE", derinc_sup, 0.0, "SWITCH", 2.0, "PARABOLIC");
		//add new group
		pg_info.insert_group(grp_name.str(), sup_rec);
		// connect super parameter to new group
		pg_info.insert_parameter_link(super_parameter_names[i_sup], grp_name.str());
	}
	return pg_info;
}

Parameters TranSVD::map_basepar_to_super(const Parameters &base_pars)
{
	Parameters super_pars;
	VectorXd base_par_vec = base_pars.get_partial_data_eigen_vec(base_parameter_names);
	VectorXd super_par_vec = Vt * base_par_vec;
	for (size_t i=0; i<super_parameter_names.size(); ++i)
	{
		super_pars[super_parameter_names[i]] = super_par_vec(i);
	}
	return super_pars;
}

TranSVD::~TranSVD()
{
	delete tran_svd_pack;
}

void TranSVD::print(ostream &os) const
{
	os << "Transformation name = " << name << "; (type=TranSVD)" << endl;
	os << "  Singular Values = " << Sigma << endl;
}

//void TranNormalize::forward(Transformable &data)
//{
//	Transformable::iterator data_iter, data_end = data.end();
//	for (map<string,NormData>::const_iterator b=items.begin(), e=items.end();
//		b!=e; ++b) {
//		data_iter = data.find(b->first);
//		if (data_iter != data_end)
//		{
//			(*data_iter).second += b->second.offset;
//			(*data_iter).second *= b->second.scale;
//		}
//	}
//}
//
//
//void TranNormalize::reverse(Transformable &data)
//{
//	Transformable::iterator data_iter, data_end = data.end();
//	for (map<string,NormData>::const_iterator b=items.begin(), e=items.end();
//		b!=e; ++b) {
//		data_iter = data.find(b->first);
//		if (data_iter != data_end)
//		{
//			(*data_iter).second /= b->second.scale;
//			(*data_iter).second -= b->second.offset;
//		}
//	}
//}
//
//
//void TranNormalize::jacobian_forward(Jacobian &jac)
//{
//	size_t icol = 0;
//	double factor = 0;
//	Transformable &data = jac.base_numeric_parameters;
//	unordered_map<string, int> par_2_col_map = jac.get_par2col_map();
//	auto iter_end = par_2_col_map.end();
//	for (const auto &irec : items)
//	{
//		auto iter = par_2_col_map.find(irec.first);
//		if (iter != iter_end)
//		{
//			icol = iter->second;
//			factor = irec.second.scale;
//			jac.matrix.col(icol) /= factor;
//		}
//	}
//	forward(data);
//}
//
//void TranNormalize::jacobian_reverse(Jacobian &jac)
//{
//	size_t icol = 0;
//	double factor = 0;
//	Transformable &data = jac.base_numeric_parameters;
//	unordered_map<string, int> par_2_col_map = jac.get_par2col_map();
//	auto iter_end = par_2_col_map.end();
//	for (const auto &irec : items)
//	{
//		auto iter = par_2_col_map.find(irec.first);
//		if (iter != iter_end)
//		{
//			icol = iter->second;
//			factor = irec.second.scale;
//			jac.matrix.col(icol) *= factor;
//		}
//	}
//	reverse(data);
//}
//
//void TranNormalize::d1_to_d2(Transformable &del_data, Transformable &data)
//{
//	Transformable::iterator del_data_iter, del_data_end = del_data.end();
//	for (map<string, NormData>::const_iterator b = items.begin(), e = items.end();
//		b != e; ++b) {
//		del_data_iter = data.find(b->first);
//		if (del_data_iter != del_data_end)
//		{
//			(*del_data_iter).second /= b->second.scale;
//		}
//	}
//	forward(data);
//}
//
//void TranNormalize::d2_to_d1(Transformable &del_data, Transformable &data)
//{
//	Transformable::iterator del_data_iter, del_data_end = del_data.end();
//	for (map<string, NormData>::const_iterator b = items.begin(), e = items.end();
//		b != e; ++b) {
//		del_data_iter = del_data.find(b->first);
//		if (del_data_iter != del_data_end)
//		{
//			(*del_data_iter).second *= b->second.scale;
//		}
//	}
//	reverse(data);
//}
//
//void TranNormalize::insert(const string &item_name, double _offset, double _scale)
//{
//	items[item_name] = NormData(_offset, _scale);
//}
//
//void TranNormalize::print(ostream &os) const
//{
//	os << "Transformation name = " << name << "; (type=TranNormalize)" << endl;
//	for (map<string,NormData>::const_iterator b=items.begin(), e=items.end();
//		b!=e; ++b) {
//			os << "  item name = " << (*b).first << ";  scale value = " << (*b).second.scale
//				<< ";  offset value = " << (*b).second.offset <<endl;
//	}
//}


//  Copyright John Maddock 2009.
//  Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/integer_traits.hpp> // must be the only #include!

template <class T>
void check_numeric_limits_derived(const std::numeric_limits<T>&){}

template <class T>
void check()
{
   typedef boost::integer_traits<T> traits;
   check_numeric_limits_derived(traits());
   bool b = traits::is_integral;
   (void)b;
   T v = traits::const_min + traits::const_max;
   (void)v;
}

int main()
{
   check<signed char>();
   check<unsigned char>();
   check<char>();
   check<short>();
   check<unsigned short>();
   check<int>();
   check<unsigned int>();
   check<signed long>();
   check<unsigned long>();
#if !defined(BOOST_NO_INTEGRAL_INT64_T) && defined(BOOST_HAS_LONG_LONG)
   check<boost::long_long_type>();
   check<boost::ulong_long_type>();
#endif
}


// (C) Copyright 2007-2009 Andrew Sutton
//
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0 (See accompanying file
// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <map>

#include <boost/graph/graph_utility.hpp>
#include <boost/graph/undirected_graph.hpp>
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/bron_kerbosch_all_cliques.hpp>
#include <boost/graph/erdos_renyi_generator.hpp>

#include <boost/random/linear_congruential.hpp>

using namespace std;
using namespace boost;

// TODO: This is probably not a very good test. We should be able to define
// an identity test - something like find the clique of K5.

struct clique_validator
{
    clique_validator()
    { }

    template <typename Clique, typename Graph>
    inline void
    clique(const Clique& c, Graph& g)
    {
        // Simply assert that each vertex in the clique is connected
        // to all others in the clique.
        typename Clique::const_iterator i, j, end = c.end();
        for(i = c.begin(); i != end; ++i) {
            for(j = c.begin(); j != end; ++j) {
                if(i != j) {
                    BOOST_ASSERT(edge(*i, *j, g).second);
                }
            }
        }
    }
};

template <typename Graph>
void test()
{
    typedef erdos_renyi_iterator<boost::minstd_rand, Graph> er;

    // Generate random graphs with 15 vertices and 15% probability
    // of edge connection.
    static const size_t N = 20;
    static const double P = 0.1;

    boost::minstd_rand rng;
    Graph g(er(rng, N, P), er(), N);
    renumber_indices(g);
    print_edges(g, get(vertex_index, g));

    bron_kerbosch_all_cliques(g, clique_validator());
}

int
main(int argc, char *argv[])
{
    typedef undirected_graph<> Graph;
    typedef directed_graph<> DiGraph;

    std::cout << "*** undirected ***\n";
    test<Graph>();

    std::cout << "*** directed ***\n";
    test<DiGraph>();
}


#include "gradient.hpp"

#include <Eigen/Dense>
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>

using namespace Eigen;

PYBIND11_MODULE(dpcxx, m) {
  pybind11::class_<GradientEval>(m, "GradientEval")
      .def(pybind11::init<const Ref<const ArrayXXd> &, const std::string &>())
      .def("compute", &GradientEval::compute);
}


#include <tradelayer/externfns.h>
#include <tradelayer/log.h>
#include <tradelayer/mdex.h>
#include <tradelayer/parse_string.h>
#include <tradelayer/tradelayer.h>
#include <tradelayer/tradelayer_matrices.h>

#include <algorithm>
#include <iostream>
#include <limits>
#include <stdint.h>
#include <string>
#include <unordered_set>

#include <boost/lexical_cast.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

mutex map_vector_mtx;
using mastercore::StrToInt64;
typedef boost::multiprecision::cpp_dec_float_100 dec_float;

using namespace std;

void printing_matrix(MatrixTLS &gdata)
{
  int M = size(gdata, 0);
  int N = size(gdata, 1);

  for (int i = 0; i < M; ++i)
    {
      for (int j = 0; j < N; ++j)
	{
	  PrintToLog("%s\t", gdata[i][j]);
	}
      PrintToLog("\n");
    }
}

void printing_vector(VectorTLS &vdata)
{
  int n = length(vdata);
  for (int i = 0; i < n; ++i) PrintToLog("%s\t", vdata[i]);
}

bool finding(std::string &s, VectorTLS &v)
{
  int n = length(v);
  bool findout = false;

  for (int i = 0; i < n; ++i)
    {
      if ( v[i] == s )
	{
	  findout = true;
	  break;
	}
    }
  return findout;
}

void sub_row(VectorTLS &jrow_databe, MatrixTLS &databe, int i)
{
  for (int j = 0; j < size(databe, 1); ++j) jrow_databe[j] = databe[i][j];
}

bool is_number(const std::string& s)
{
  std::string::const_iterator it = s.begin();
  while (it != s.end() && std::isdigit(*it)) ++it;
  return !s.empty() && it == s.end();
}

bool finding_string(std::string sub_word, std::string word_target)
{
  bool word_bool = false;

  std::size_t found = word_target.find(sub_word);
  if (found != std::string::npos)
    word_bool = true;

  return word_bool;
}

bool find_string_strv(std::string s, std::vector<std::string> v)
{
  bool find_bool = false;
  for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)
    {
      if ( finding_string(s, *it) )
	{
	  find_bool = true;
	  break;
	}
    }
  return find_bool;
}

bool find_uint64_t(uint64_t m, std::vector<uint64_t> v)
{
  bool find_bool = false;
  for (std::vector<uint64_t>::iterator it = v.begin(); it != v.end(); ++it)
    {
      if ( *it == m )
	{
	  find_bool = true;
	  break;
	}
    }
  return find_bool;
}

bool find_string_set(std::string s, std::unordered_set<std::string> addrs_set)
{
  bool find_bool = false;
  for (const std::string &x : addrs_set)
    {
      if ( finding_string(s, x) )
	{
	  find_bool = true;
	  break;
	}
    }
  return find_bool;
}

bool findTrueValue(bool a, bool b) { return a || b; }
bool findTrueValue(bool a, bool b, bool c) { return ( a || b ) || c; }
bool findTrueValue(bool a, bool b, bool c, bool d) { return ( a || b ) || ( c || d ); }
bool findConjTrueValue(bool a, bool b, bool c) { return ( a && b ) && c; }
bool findConjTrueValue(bool a, bool b) { return a && b; }

void threading(uint32_t keyid, int64_t valueid, std::string name_vec)
{
  if (name_vec == "cdex_volume")
    {
      map_vector_mtx.lock();
      mapContractVolume[keyid].push_back(valueid);
      map_vector_mtx.unlock();
    }
  else if (name_vec == "cdex_price")
    {
      map_vector_mtx.lock();
      mapContractAmountTimesPrice[keyid].push_back(valueid);
      map_vector_mtx.unlock();
    }
  else if (name_vec == "cdex_vwap")
    {
      map_vector_mtx.lock();
      VWAPMapContracts[keyid]=valueid;
      map_vector_mtx.unlock();
    }
}

void vwap_num_den(std::vector<int64_t> vn, std::vector<int64_t> vd, int64_t &n, int64_t &d)
{
  for (unsigned int i = 0; i < vn.size(); i++)
    {
      n += vn[i];
      d += vd[i];
    }
}

namespace mastercore
{
  int64_t DoubleToInt64(double d) { return mastercore::StrToInt64(boost::lexical_cast<std::string>(d), true); }
  int64_t RationalToInt64(rational_t r) { return mastercore::StrToInt64(xToString(r), true); }
}


#include "CG/CGSolver.h"
#include <EigenSolver/EigenSolver.h>
#include <EigenSolver/Miscellaneous.h>
void fun2(int *a,int **b)
{
  //b=&a;
  **b=*a;
  std::cout<<"This is value of b"<<**b<<std::endl;
}

void fun3(int &w, int **b)
{
  **b=w;
}

int main()
{
  int a=2312321,c=2;
  int *b=&c;
  //b=&a;
  // fun3(a,&b);
  std::cout<<*b<<std::endl;
  CGSolver AAA;
  dealii::SparseMatrix<double> A, A2, M, M2;
  // std::ofstream sparsematrix1 ("original_matrix.1");
  //A.print(sparsematrix1);
  std::vector<unsigned int> row_length(10,10);
  unsigned int t=3;
  dealii::SparsityPattern sparsity_pattern(10,10,{2,3,3,3,3,3,3,3,3,2});
  dealii::SparsityPattern sp2(3,3,{2,3,2});
  dealii::SparsityPattern sp3(3,3,{1,1,1}), sp4(10,10,{1,1,1,1,1,1,1,1,1,1});
  sp2.add(0,1);
  sp2.add(1,0);
  sp2.add(1,2);
  sp2.add(2,1);
  //sparsity_pattern.add(1,2);
  sparsity_pattern.add(0,1);
  sparsity_pattern.add(9,8);
  for (int i=1;i<9;i++)
    {
      sparsity_pattern.add(i,i+1);
      sparsity_pattern.add(i,i-1);
    }

  A.reinit(sparsity_pattern);
  M.reinit(sp2);
  A2.reinit(sp3);
  M2.reinit(sp4);
    // In this way, I can construct a SparseMatrix to be the test data for the algorithm.
  for (int k=0;k<A.m();k++)
    {
      dealii::SparseMatrix<double>::iterator i=A.begin(k);
      i->value()=2;
      while(i!=A.end(k))
	{
	  i->value()+=1;
	  ++i;
	}
    }

      // In this way, I can construct a SparseMatrix to be the test data for the algorithm.

 // To construct the matrix M as diag(2,2,2) with subdiagonal entries -1.
 /*
  for (int k=0;k<M.m();k++)
    {
      dealii::SparseMatrix<double>::iterator i=M.begin(k);
      i->value()=3;
      while(i!=M.end(k))
	{
	  i->value()-=1;
	  ++i;
	}
    }
    */
  for (int k=0;k<M.m();k++)
    {
      dealii::SparseMatrix<double>::iterator i=M.begin(k);
      i->value()=20.0*(k+1);
      while(i!=M.end(k))
        {
          i->value()-=1;
	  ++i;
        }
    }

  for (int k=0;k<M2.m();k++)
    {
      dealii::SparseMatrix<double>::iterator i=M2.begin(k);
      i->value()=k+1;

    }

 // Construct the right hand side matrix in example as A2.
    for (int k=0;k<A2.m();k++)
    {
      dealii::SparseMatrix<double>::iterator i=A2.begin(k);
      i->value()=(4*k)+1;
      //i->value()=1;
    }

  //std::cout<<AAA.tolerence()<<std::endl;
  //CGSolver solve(A), sol2(M);
  EigenSolver sol3(M,A2),sol4(A,M2);
  std::ofstream sparsematrix2 ("sparse_matrix.2");
  A.print(sparsematrix2);
  //std::cout<<solve.A->n()<<std::endl;
  dealii::Vector<double> x0(10),B(10), x1(3),B2(3);
  for(int i=0;i<x1.size();i++)
  {
    std::cout<<x1[i]<<" ";
  }
  std::cout<<"\n";
  B[0]=4;
  for(int i=1;i<9;i++)
    {
      B[i]=5;
    }
  B[9]=4;
  //B2[0]=1;
  //B2[2]=1.8;
  B2[0]=-2;
  B2[1]=4;
  B2[2]=-2;
  std::cout<<"test1\n";
  //solve.solve(x0,B,1.0e-12,100);
  //sol2.solve(x1,B2,1.0e-3,20);
  /*
  for (int k=0;k<10;k++)
    {
      std::cout<<x0[k]<<" ";
    }
  std::cout<<"\n";

  for (int k=0;k<3;k++)
    {
        std::cout<<x1[k]<<" ";
    }
  std::cout<<"\n";
  */
  std::vector<double> xx(3,1);
  double lambda = 0;
  std::cout<<"11\n";
  sol3.PowerSolve(xx, lambda);
  std::cout<<"22\n";
  for (int k=0;k<xx.size();k++)
  {
    std::cout<<xx[k]<<" ";
  }
  std::cout<<"\n";
  std::vector<double> x2(3,1);
  sol3.IPowerSolve(x2,lambda);
    for (int k=0;k<x2.size();k++)
  {
    std::cout<<x2[k]<<" ";
  }
  std::cout<<"\n";
  int eig_num=3;
  std::vector<double> tempxx(eig_num, 0), lam_3(eig_num,0);
  std::vector<std::vector<double>> x3(3, tempxx);
  x3[0][0]=1;
  x3[1][1]=1;
  x3[2][2]=1;
  for (int i=0;i<x3.size();i++)
  {
    for(int j=0;j<x3[0].size();j++)
    {
      x3[i][j] = ((double) rand() / (RAND_MAX));
    }
  }
  std::cout<<"The initial matrix x3 is :: \n";
  show_matrix(x3);
  sol3.BIPowerSolve(x3,lam_3,eig_num,100);
    for (int k=0;k<eig_num;k++)
  {
    std::cout<<lam_3[k]<<" ";
  }
  std::cout<<"\n";
  std::cout<<"The eigenvectors are:\n";
  show_matrix(x3);

  eig_num=3;
  std::vector<double> tempx4(eig_num, 0), lam_4(eig_num,0);
  std::vector<std::vector<double>> x4(A.m(), tempxx);
  //x3[0][0]=1;
  //x3[1][1]=1;
  //x3[2][2]=1;
  for (int i=0;i<x4.size();i++)
  {
    for(int j=0;j<x4[0].size();j++)
    {
      x4[i][j] = ((double) rand() / (RAND_MAX));
    }
  }
  std::cout<<"The initial matrix x4 is :: \n";
  show_matrix(x4);
  sol4.LOBPCGSolve(x4,lam_4,eig_num,100);
    for (int k=0;k<eig_num;k++)
  {
    std::cout<<lam_4[k]<<" ";
  }
  std::cout<<"\n";

 // std::cout<<"The eigenvectors are:\n";
 // show_matrix(x3);

  // std::cout<<(*P).n()<<std::endl;
  std::ofstream sparsematrix ("sparse_matrix.2");
  M.print(sparsematrix);
  std::ofstream sparsematrixA ("sparse_matrixA");
  A2.print(sparsematrixA);

  std::ofstream sparsematrix4 ("matrixA");
  A.print(sparsematrix4);

  std::filebuf fb;
  fb.open ("stiff_matrix.txt",std::ios::out);
  std::ostream os(&fb);
  A.print_formatted(os, 3, true, 0, "0.0", 1);
  fb.close();

  std::filebuf fb2;
  fb2.open ("mass_matrix.txt",std::ios::out);
  std::ostream os2(&fb2);
  M2.print_formatted(os2, 3, true, 0, "0.0", 1);
  fb2.close();

  return 0;
}


#include <boost/test/unit_test.hpp>

#include "transport.hpp"

BOOST_AUTO_TEST_SUITE(TransportTests)

    BOOST_AUTO_TEST_CASE(Example1) {
        std::vector<int> supply = { 5, 3, 8 };
        std::vector<int> demand = { 4, 10, 2 };
        std::vector<int> costs = {
            10, 3, 17,
            5, 12, 4,
            3, 18, 4
        };
        std::vector<int> flow(9);

        solveTransport(3, 3, costs.data(),
                supply.data(), demand.data(), flow.data());

        std::vector<int> expectedFlow = {
            0, 5, 0,
            0, 3, 0,
            4, 2, 2
        };

        for (int i = 0; i < 9; ++i)
            BOOST_CHECK_EQUAL(flow[i], expectedFlow[i]);

    }

BOOST_AUTO_TEST_SUITE_END()


/*
 * added by Shufang ZHU on Jan 10th, 2017
 * translate ltlf formulas to fol, the input of MONA
*/

#include "ltlf2fol.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <set>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <boost/algorithm/string.hpp>

using namespace std;
#define MAXN 1000000

void ltlf2fol (ltl_formula *root)
{
  int c = 1;
  string res;

  set<string> P = get_alphabet (root);
  if(!P.empty()){
    // cout<<"var2 ALIVE, ";
    cout<<"m2l-str;"<<endl;
    cout<<"var2 ";
    print_alphabet_no_comma(root);
    cout<<";"<<endl;
    // cout<<"allpos ALIVE;"<<endl;
    // cout<<"0 in ALIVE;"<<endl;
  }

  res = trans_fol(root, 0, c);
  cout<<res<<";"<<endl;

}

void print_alphabet_no_comma (ltl_formula* root){
  set<string> P = get_alphabet (root);
  if(!P.empty()){
    set<string>::iterator it = P.begin ();
    // cout<<toupper(*it);
    cout<<up(*it);
    it++;
    while (it != P.end ()){
      cout<<", "<<up(*it);
      it++;
    }
  }
}

string alphabet_no_comma (ltl_formula* root){
  string res = "";
  set<string> P = get_alphabet (root);
  if(!P.empty()){
    set<string>::iterator it = P.begin ();
    // cout<<toupper(*it);
    res += up(*it);
    it++;
    while (it != P.end ()){
      res += ", "+up(*it);
      it++;
    }
  }
  return res;
}

void print_alphabet (ltl_formula* root){
  set<string> P = get_alphabet (root);
  if(!P.empty()){
    set<string>::iterator it = P.begin ();
    // cout<<toupper(*it);
    cout<<", "<<up(*it);
    it++;
    while (it != P.end ()){
      cout<<", "<<up(*it);
      it++;
    }
  }
}

void print_alphabet_not (ltl_formula* root){
  set<string> P = get_alphabet (root);
  if(!P.empty()){
    set<string>::iterator it = P.begin ();
    // cout<<toupper(*it);
    cout<<", "<<up(*it)<<"\\{p}";
    it++;
    while (it != P.end ()){
      cout<<", "<<up(*it);
      it++;
    }
  }
}

void printvars (ltl_formula* root){
  set<string> P = get_alphabet (root);
  if(!P.empty()){
    set<string>::iterator it = P.begin ();
    // cout<<toupper(*it);
    cout<<", var2 "<<up(*it);
    it++;
    while (it != P.end ()){
      cout<<", var2 "<<up(*it);
      it++;
    }
  }
}

string trans_fol(ltl_formula* root, int t, int& c){
  string curs, ts;
  string exs, alls;
  string res;
  int cur;
  switch(root->_type)
  {
        case eNOT:
          res = "~(";
          res += trans_fol(root->_right, t, c);
          res += ")";
          break;
        case eNEXT:
          exs = "x"+to_string(t+1);
          if (t == 0)
            ts = to_string(t);
          else
            ts = "x"+to_string(t);
          res = "(ex1 "+exs+": ("+exs+"="+ts+"+1 & (";
          res += trans_fol(root->_right, t+1, c);
          res += ")))";
          break;
        case eWNEXT:
          exs = "x"+to_string(t+1);
          if (t == 0)
            ts = to_string(t);
          else
            ts = "x"+to_string(t);
          res = "((ex1 "+exs+": ("+exs+"="+ts+"+1 & (";
          res += trans_fol(root->_right, t+1, c);
          res += "))) | ("+ts+" = max $))";
          break;
        case eFUTURE:
          exs = "x"+to_string(t+1);
          if (t == 0)
            ts = to_string(t);
          else
            ts = "x"+to_string(t);
          res = "(ex1 "+exs+": ("+ts+" <= "+exs+" & (";
          res += trans_fol(root->_right, t+1, c);
          res += ")))";
          break;
        case eGLOBALLY:
          alls = "x"+to_string(t+1);
          if (t == 0)
            ts = to_string(t);
          else
            ts = "x"+to_string(t);
          res = "(all1 "+alls+": (("+ts+" <= "+alls+") => (";
          res += trans_fol(root->_right, t+1, c);
          res += ")))";
          break;
        case eUNTIL:
          exs = "x"+to_string(t+1);
          alls = "x"+to_string(t+2);
          if (t == 0)
            ts = to_string(t);
          else
            ts = "x"+to_string(t);
          res = "(ex1 "+exs+": ("+ts+" <= "+exs+" & (";
          res += trans_fol(root->_right, t+1, c);
          res += ") & (all1 "+alls+": ("+ts+" <= "+alls+" & "+alls;
          res += " < "+exs+" => (";
          res += trans_fol(root->_left, t+2, c);
          res += ")))))";
          break;
        case eRELEASE: //New
          exs = "x"+to_string(t+1);
          alls = "x"+to_string(t+2);
          if (t == 0)
            ts = to_string(t);
          else
            ts = "x"+to_string(t);
          res = "((ex1 "+exs+": ("+ts+" <= "+exs+" & (";
          res += trans_fol(root->_left, t+1, c);
          res += ") & (all1 "+alls+": ("+ts+" <= "+alls+" & "+alls;
          res += " <= "+exs+" => (";
          res += trans_fol(root->_right, t+2, c);
          res += ")))))";
          res += "| (all1 "+alls+": (("+ts+" <= "+alls+" & "+alls+" <= max $) => (";
          res += trans_fol(root->_right, t+2, c);
          res += "))))";
          break;
        case eOR:
          res += "(("+trans_fol(root->_right, t, c);
          res += ") | (";
          res += trans_fol(root->_left, t, c)+"))";
          break;
        case eAND:
          res += "(("+trans_fol(root->_right, t, c);
          res += ") & (";
          res += trans_fol(root->_left, t, c)+"))";
          break;
        case eTRUE:
          res += "(true)";
          break;
        case eFALSE:
          res += "(false)";
          break;
        case 3:
          if (t == 0)
            ts = "("+to_string(t);
          else
            ts = "(x"+to_string(t);
          res += ts+" in ";
          res += alphabet_no_comma(root);
          res +=")";
          break;
        default:
          break;
  }
  // cout<<res<<endl;
  return res;
}

string up(string a){
  return boost::to_upper_copy<std::string>(a);
}

char in[MAXN];

int main (int argc, char ** argv)
{

		string StrLine;
		std::string input;
    std::string format;
		if(argc != 3){
        cout<<"Usage: ./ltlf2fol format(NNF, BNF) filename"<<endl;
        return 0;
    }
		input = argv[2];
    format = argv[1];
		ifstream myfile(input);
		if (!myfile.is_open()) //判断文件是否存在及可读
		{
		    printf("unreadable file!");
		    return -1;
		}
		getline(myfile, StrLine);
		myfile.close(); //关闭文件
    strcpy (in, StrLine.c_str());
    printf ("#%s\n", in);

    ltl_formula *root = getAST (in);
    ltl_formula *bnfroot = bnf (root);
    ltl_formula *newroot;
    printf ("#%s\n", to_string (bnfroot).c_str ());
    if(format == "NNF"){
      printf ("#NNF format\n");
      newroot = nnf (bnfroot) ;
    }
    else{
      printf ("#BNF format\n");
      newroot = bnfroot;
    }

    printf ("#%s\n", to_string (newroot).c_str ());
    ltlf2fol (newroot);

    // printf ("%s\n", res.c_str ());
    destroy_formula (root);
    destroy_formula (newroot);
    //destroy_formula (nnfroot);
}


#include <iostream>
#include <Eigen/Core>
#include <bench/BenchUtil.h>
using namespace Eigen;

#ifndef REPEAT
#define REPEAT 100000
#endif

#ifndef TRIES
#define TRIES 20
#endif

typedef double Scalar;

template <typename MatrixType>
__attribute__ ((noinline)) void bench_reverse(const MatrixType& m)
{
  int rows = m.rows();
  int cols = m.cols();
  int size = m.size();

  int repeats = (REPEAT*1000)/size;
  MatrixType a = MatrixType::Random(rows,cols);
  MatrixType b = MatrixType::Random(rows,cols);

  BenchTimer timerB, timerH, timerV;

  Scalar acc = 0;
  int r = ei_random<int>(0,rows-1);
  int c = ei_random<int>(0,cols-1);
  for (int t=0; t<TRIES; ++t)
  {
    timerB.start();
    for (int k=0; k<repeats; ++k)
    {
      asm("#begin foo");
      b = a.reverse();
      asm("#end foo");
      acc += b.coeff(r,c);
    }
    timerB.stop();
  }

  if (MatrixType::RowsAtCompileTime==Dynamic)
    std::cout << "dyn   ";
  else
    std::cout << "fixed ";
  std::cout << rows << " x " << cols << " \t"
            << (timerB.value() * REPEAT) / repeats << "s "
            << "(" << 1e-6 * size*repeats/timerB.value() << " MFLOPS)\t";

  std::cout << "\n";
  // make sure the compiler does not optimize too much
  if (acc==123)
    std::cout << acc;
}

int main(int argc, char* argv[])
{
  const int dynsizes[] = {4,6,8,16,24,32,49,64,128,256,512,900,0};
  std::cout << "size            no sqrt                           standard";
//   #ifdef BENCH_GSL
//   std::cout << "       GSL (standard + double + ATLAS)  ";
//   #endif
  std::cout << "\n";
  for (uint i=0; dynsizes[i]>0; ++i)
  {
    bench_reverse(Matrix<Scalar,Dynamic,Dynamic>(dynsizes[i],dynsizes[i]));
    bench_reverse(Matrix<Scalar,Dynamic,1>(dynsizes[i]*dynsizes[i]));
  }
//   bench_reverse(Matrix<Scalar,2,2>());
//   bench_reverse(Matrix<Scalar,3,3>());
//   bench_reverse(Matrix<Scalar,4,4>());
//   bench_reverse(Matrix<Scalar,5,5>());
//   bench_reverse(Matrix<Scalar,6,6>());
//   bench_reverse(Matrix<Scalar,7,7>());
//   bench_reverse(Matrix<Scalar,8,8>());
//   bench_reverse(Matrix<Scalar,12,12>());
//   bench_reverse(Matrix<Scalar,16,16>());
  return 0;
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <nt2/trigonometric/include/functions/rem_pio2_straight.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <nt2/include/functions/unary_minus.hpp>
#include <nt2/include/constants/pio_2.hpp>
#include <nt2/include/constants/pio_4.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/sdk/meta/as_integer.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/ulp.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

NT2_TEST_CASE_TPL ( rem_pio2_straight_real__1_0,  NT2_SIMD_REAL_TYPES)
{
  using nt2::rem_pio2_straight;
  using nt2::tag::rem_pio2_straight_;
  using boost::simd::native;
  typedef typename nt2::meta::as_integer<T>::type           iT;
  typedef native<T,BOOST_SIMD_DEFAULT_EXTENSION>            vT;
  typedef native<iT,BOOST_SIMD_DEFAULT_EXTENSION>          viT;

  {
    vT r1;
    NT2_TEST_EQUAL( rem_pio2_straight(nt2::Pio_2<vT>(), r1), nt2::One<viT>());
    NT2_TEST_ULP_EQUAL( r1, nt2::Zero<vT>(), 0.5);
    NT2_TEST_EQUAL( rem_pio2_straight(nt2::Pio_4<vT>(), r1), nt2::One<viT>());
    NT2_TEST_ULP_EQUAL( r1, -nt2::Pio_4<vT>(), 0.5);
  }

}


//  (C) Copyright Raffi Enficiaud 2019.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.

//[example_code
#define BOOST_TEST_MODULE example83 multicontext
#include <boost/test/included/unit_test.hpp>
#include <cmath>

BOOST_AUTO_TEST_CASE(test_multi_context)
{
  for (int level = 0; level < 10; ++level) {
    int rand_value = std::abs(rand()) % 50;
    BOOST_TEST_CONTEXT("With level " << level, "Random value=" << rand_value){
      for( int j = 1; j < rand_value; j++) {
        BOOST_TEST(level < rand_value);
        rand_value /= 2;
      }
    }
  }
}
//]


#include "inner_prod_test.hpp"
#include <boost/numeric/ublas/matrix.hpp>

int main()
{
  ///testing row major int inner prod
  bench_inner_prod<int, 10, 10> b1;

  ///testing row major float inner prod
  bench_inner_prod<float, 10, 10> b2;

  ///testing row major double inner prod
  bench_inner_prod<double, 10, 10> b3;

  b1.run();
  b2.run();
  b3.run();

  return 0;

}


/* Copyright (c) 2017, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 *
 * All rights reserved.
 *
 * The Astrobee platform is licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

#include <common/init.h>
#include <sparse_mapping/sparse_map.h>

#include <Eigen/Geometry>
#include <gflags/gflags.h>
#include <glog/logging.h>

#include <fstream>

DEFINE_bool(show_delta_affines, false,
            "Instead of printing the camera location as an affine,"
            " show the affine between the cameras. Should allow for"
            " easier comparison between map solutions.");
DEFINE_string(show_spec_triplet, "",
              "Specify a triplet that you want to see, ex: '0_3_43'");

int main(int argc, char** argv) {
  common::InitFreeFlyerApplication(&argc, &argv);

  // We require a map input
  if (argc < 2) {
    LOG(ERROR) << "Usage: " << argv[0] << " map.map";
    return 1;
  }

  // Check if input exists
  std::ifstream f(argv[1]);
  if (!f.good()) {
    LOG(FATAL) << "File does not exist: " << argv[1];
  }

  // Load up the map file
  std::string map_file = argv[1];
  sparse_mapping::SparseMap map(map_file);
  LOG(INFO) << "Loaded " << argv[1];
  LOG(INFO) << "\t" << map.GetNumFrames() << " Cameras and "
            << map.GetNumLandmarks() << " Points";

  const Eigen::IOFormat csv_format(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", ", ");

  if (FLAGS_show_delta_affines) {
    std::ofstream of(map_file + ".delta.csv");
    double scaling = 0;
    for (size_t cid = 0; cid < map.GetNumFrames() - 1; cid++) {
      Eigen::Affine3d cam_n_t_global = map.GetFrameGlobalTransform(cid);
      Eigen::Affine3d cam_n1_t_global = map.GetFrameGlobalTransform(cid + 1);
      Eigen::Affine3d delta = (cam_n1_t_global * cam_n_t_global.inverse());

      if (cid == 0) {
        scaling = 1.0 / delta.translation().norm();
      }
      delta.translation() *= scaling;

      LOG(INFO) << "CID " << cid << " cid_n+1_t_cid_n:\n"
                << delta.matrix();
      of << delta.matrix().format(csv_format) << std::endl;
    }
  } else if (!FLAGS_show_spec_triplet.empty()) {
    std::vector<std::string> tokens;
    std::istringstream iss(FLAGS_show_spec_triplet);
    std::string holder;
    while (std::getline(iss, holder, '_')) {
      tokens.push_back(holder);
    }
    if (tokens.size() != 3)
      LOG(FATAL) << "Found incorrect number of tokens: " << tokens.size();

    int cid1 = std::stoi(tokens[0]);
    int cid2 = std::stoi(tokens[1]);
    int cid3 = std::stoi(tokens[2]);

    Eigen::Affine3d
      j_t_i = map.GetFrameGlobalTransform(cid2) * map.GetFrameGlobalTransform(cid1).inverse(),
      k_t_i = map.GetFrameGlobalTransform(cid3) * map.GetFrameGlobalTransform(cid1).inverse(),
      k_t_j = map.GetFrameGlobalTransform(cid3) * map.GetFrameGlobalTransform(cid2).inverse();
    double scale = 1.0 / j_t_i.translation().norm();
    j_t_i.translation() *= scale;
    k_t_i.translation() *= scale;
    k_t_j.translation() *= scale;

    LOG(INFO) << "Affine: " << cid1 << " " << cid2 << "\n"
              << j_t_i.matrix();
    LOG(INFO) << "Affine: " << cid1 << " " << cid3 << "\n"
              << k_t_i.matrix();
    LOG(INFO) << "Affine: " << cid2 << " " << cid3 << "\n"
              << k_t_j.matrix();
  } else {
    std::ofstream of(map_file + ".abs.csv");
    for (size_t cid = 0; cid < map.GetNumFrames(); cid++) {
      Eigen::Affine3d cam_t_global = map.GetFrameGlobalTransform(cid);
      LOG(INFO) << "CID " << cid << " cam_t_global:\n"
                << cam_t_global.matrix();
      of << cam_t_global.matrix().format(csv_format) << std::endl;
    }
  }

  return 0;
}


/*
The MIT License

Copyright (c) 2015-2016 Albert Murienne

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "network_bnu_base.h"

#include "common/network_config.h"
#include "common/network_exception.h"
#include "common/network_random.h"

#include <boost/optional.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>

namespace bnu = boost::numeric::ublas;

namespace neurocl { namespace mlp {

const std::string dump_mat( const matrixF& mat, boost::optional<std::string> label = boost::none )
{
    std::string separator;
    std::stringstream ss;
    ss << ( label ? label.get() : "" ) << std::endl;
    for( matrixF::const_iterator1 it1 = mat.cbegin1(); it1 != mat.cend1(); ++it1 )
    {
        for( matrixF::const_iterator2 it2 = it1.begin(); it2 !=it1.end(); ++it2 )
        {
            ss << separator << *it2;
            separator = " ";
        }
        separator = "";
        ss << std::endl;
    }
    return ss.str();
}

const std::string dump_vec( const vectorF& vec, boost::optional<std::string> label = boost::none )
{
    std::string separator;
    std::stringstream ss;
    ss << ( label ? label.get() : "" ) << std::endl;
    for( auto val : vec )
    {
            ss << separator << val;
            separator = " ";
    }
    ss << std::endl;
    return ss.str();
}

template<class T>
void random_normal_init( T& container, const float stddev = 1.f )
{
    random::rand_gaussian_generator rgg( 0.f, stddev );

    for( auto& element : container.data() )
    {
        element = rgg();
    }
}

layer_bnu::layer_bnu()
{
}

// WARNING : size is the square side size
void layer_bnu::populate( const layer_size& cur_layer_size, const layer_size& next_layer_size )
{
    //LOGGER(info) << "layer_bnu::populate - populating layer of size " << cur_layer_size << " (next size is " << next_layer_size << ")" << std::endl;

    if ( next_layer_size.size() ) // non-output layer
    {
        m_output_weights = matrixF( next_layer_size.size(), cur_layer_size.size() );
        // cf. http://neuralnetworksanddeeplearning.com/chap3.html#weight_initialization
        random_normal_init( m_output_weights, 1.f / std::sqrt( cur_layer_size.size() ) );
        m_deltas_weight = matrixF( next_layer_size.size(), cur_layer_size.size() );
        m_deltas_weight.clear();

        m_bias = vectorF( next_layer_size.size() );
        random_normal_init( m_bias, 1.f );
        m_deltas_bias = vectorF( next_layer_size.size() );
        m_deltas_bias.clear();
    }

    m_activations = vectorF( cur_layer_size.size() );
    m_activations.clear();
    m_errors = vectorF( cur_layer_size.size() ); // not needed for input layer...?
    m_errors.clear();
}

const std::string layer_bnu::dump_weights() const
{
    return dump_mat( m_output_weights );
}

const std::string layer_bnu::dump_bias() const
{
    return dump_vec( m_bias );
}

const std::string layer_bnu::dump_activations() const
{
    return dump_vec( m_activations );
}

network_bnu_base::network_bnu_base() : m_training_samples( 0 ), m_learning_rate( 3.0f/*0.01f*/ ), m_weight_decay( 0.0f )
{
    const network_config& nc = network_config::instance();
    nc.update_optional( "learning_rate", m_learning_rate );
}

void network_bnu_base::set_input(  const size_t& in_size, const float* in )
{
    if ( in_size > m_layers[0].activations().size() )
        throw network_exception( "sample size exceeds allocated layer size!" );

    //LOGGER(info) << "network_bnu::set_input - input (" << in << ") size = " << in_size << std::endl;

    vectorF& input_activations = m_layers[0].activations();
    std::copy( in, in + in_size, input_activations.begin() );
}

void network_bnu_base::set_output( const size_t& out_size, const float* out )
{
    if ( out_size > m_training_output.size() )
        throw network_exception( "output size exceeds allocated layer size!" );

    //LOGGER(info) << "network_bnu::set_output - output (" << out << ") size = " << out_size << std::endl;

    std::copy( out, out + out_size, m_training_output.begin() );
}

void network_bnu_base::add_layers_2d( const std::vector<layer_size>& layer_sizes )
{
    m_layers.resize( layer_sizes.size() );

    // Last layer should be output layer
    const layer_size& _last_size = layer_sizes.back();
    m_layers.back().populate( _last_size, layer_size( 0, 0 ) );

    // Initialize training output
    m_training_output = vectorF( _last_size.size() );

    // Populate all but input layer
    for ( int idx=layer_sizes.size()-2; idx>=0; idx-- )
    {
        const layer_size& _size = layer_sizes[idx];
        const layer_size& _next_layer_size = layer_sizes[idx+1];
        m_layers[idx].populate( _size, _next_layer_size );
    }
}

const layer_ptr network_bnu_base::get_layer_ptr( const size_t layer_idx )
{
    if ( layer_idx >= m_layers.size() )
    {
        LOGGER(error) << "network_bnu_base::get_layer_ptr - cannot access layer " << layer_idx << std::endl;
        throw network_exception( "invalid layer index" );
    }

    matrixF& weights = m_layers[layer_idx].weights();
    vectorF& bias = m_layers[layer_idx].bias();
    layer_ptr l( weights.size1() * weights.size2(), bias.size() );
    std::copy( &weights.data()[0], &weights.data()[0] + ( weights.size1() * weights.size2() ), l.weights.get() );
    std::copy( &bias[0], &bias[0] + bias.size(), l.bias.get() );

    return l;
}

void network_bnu_base::set_layer_ptr( const size_t layer_idx, const layer_ptr& layer )
{
    if ( layer_idx >= m_layers.size() )
    {
        LOGGER(error) << "network_bnu_base::set_layer_ptr - cannot access layer " << layer_idx << std::endl;
        throw network_exception( "invalid layer index" );
    }

    LOGGER(info) << "network_bnu_base::set_layer_ptr - setting layer  " << layer_idx << std::endl;

    matrixF& weights = m_layers[layer_idx].weights();
    std::copy( layer.weights.get(), layer.weights.get() + layer.num_weights, &weights.data()[0] );
    vectorF& bias = m_layers[layer_idx].bias();
    std::copy( layer.bias.get(), layer.bias.get() + layer.num_bias, &bias.data()[0] );
}

const output_ptr network_bnu_base::output()
{
    vectorF& output = m_layers.back().activations();
    output_ptr o( output.size() );
    std::copy( &output[0], &output[0] + output.size(), o.outputs.get() );

    return o;
}

void network_bnu_base::clear_gradients()
{
    // Clear gradients
    for ( size_t i=0; i<m_layers.size()-1; i++ )
    {
        m_layers[i].w_deltas().clear();
        m_layers[i].b_deltas().clear();
    }

    m_training_samples = 0;
}

float network_bnu_base::loss()
{
    vectorF& output = m_layers.back().activations();
    vectorF loss = bnu::element_prod( output - m_training_output, output - m_training_output );

    float _acc = 0.f;
    std::for_each( loss.begin(), loss.end(),
        [&_acc] ( float a ) {
            _acc += a;
        });

    return 0.5f * _acc / static_cast<float>( loss.size() );
}

const std::string network_bnu_base::dump_weights()
{
    std::stringstream ss;
    ss << "*************************************************" << std::endl;
    for( const auto& layer : m_layers )
    {
        ss << layer.dump_weights();
        ss << "-------------------------------------------------" << std::endl;
    }
    ss << "*************************************************" << std::endl;
    return ss.str();
}

const std::string network_bnu_base::dump_bias()
{
    std::stringstream ss;
    ss << "*************************************************" << std::endl;
    for( const auto& layer : m_layers )
    {
        ss << layer.dump_bias();
        ss << "-------------------------------------------------" << std::endl;
    }
    ss << "*************************************************" << std::endl;
    return ss.str();
}

const std::string network_bnu_base::dump_activations()
{
    std::stringstream ss;
    ss << "*************************************************" << std::endl;
    for( const auto& layer : m_layers )
    {
        ss << layer.dump_activations();
        ss << "-------------------------------------------------" << std::endl;
    }
    ss << "*************************************************" << std::endl;
    return ss.str();
}

void network_bnu_base::gradient_check( const output_ptr& out_ref )
{
    LOGGER(error) << "network_bnu_base::gradient_check - not implemented yet for MLP" << std::endl;
}

} /*namespace neurocl*/ } /*namespace mlp*/


// Copyright (c) 2018 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE AllTrueTest
#include <boost/test/unit_test.hpp>
#include <poplar/Engine.hpp>
#include <poplibs_support/TestDevice.hpp>
#include <popops/AllTrue.hpp>
#include <popops/codelets.hpp>
#include <poputil/exceptions.hpp>

using namespace poplar;
using namespace poplar::program;
using namespace poputil;
using namespace popops;
using namespace poplibs_support;

#define DIM_SIZE 4

bool allTrueTest(bool in[DIM_SIZE]) {
  auto device = createTestDevice(TEST_TARGET);
  Graph graph(device.getTarget());
  popops::addCodelets(graph);

  Tensor tIn = graph.addVariable(BOOL, {DIM_SIZE}, "t1");
  graph.setTileMapping(tIn, 0);

  auto seq = Sequence();
  const auto tOut = allTrue(graph, tIn, seq, "");

  graph.createHostWrite("in", tIn);
  graph.createHostRead("out", tOut);

  bool out;
  Engine eng(graph, seq);
  device.bind([&](const Device &d) {
    eng.load(d);
    eng.writeTensor("in", in, &in[DIM_SIZE]);
    eng.run();
    eng.readTensor("out", &out, &out + 1);
  });
  return out;
}

BOOST_AUTO_TEST_CASE(AllTrue) {
  bool in[DIM_SIZE] = {true, true, true, true};
  BOOST_CHECK_EQUAL(allTrueTest(in), true);
}

BOOST_AUTO_TEST_CASE(NotAllTrue) {
  bool in[DIM_SIZE] = {true, false, false, true};
  BOOST_CHECK_EQUAL(allTrueTest(in), false);
}