/**
 * \file SMTpass.cc
 * \brief Implementation of the SMTpass pass
 * \author Julien Henry
 */
#include <sstream>
#include <vector>
#include <queue>
#include <iostream>
#include <string>
#include <limits>
#include <cmath>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include "llvm/Analysis/CFG.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"

#include "Pr.h"
#include "SMTpass.h"
#include "z3_manager.h"
#include "yices.h"
#include "SMTlib.h"
#include "Expr.h"
#include "apron.h"
#include "Debug.h"

/*
DM: If set to 0, modulo (grid) constraints are not converted to SMT.
If set to 1, they are - this causes segfaults in Z3 3.8 (but not 4.0+).
*/
#define SMT_HAS_WORKING_MODULO 1

using namespace std;
using namespace boost;

std::map<BasicBlock*,int> NodeNames;
std::map<int,BasicBlock*> NodeAddress;

int CurrentNodeName;

int SMTpass::nundef = 0;

SMTpass::SMTpass() {
	switch (getSMTSolver()) {
#if HAS_Z3
		case API_Z3:
			man = new z3_manager();
			break;
#endif
#ifdef HAS_YICES
		case API_YICES:
			man = new yices();
			break;
#endif
		default:
			man = new SMTlib();
	}
	stack_level = 0;
	CurrentNodeName = 0;
}

SMTpass::~SMTpass() {
	delete man;
}

SMTpass * instance = NULL;
SMTpass * instanceforAbstract = NULL;

SMTpass * SMTpass::getInstance() {
	if (instance == NULL)
		instance = new SMTpass();
	return instance;
}

SMTpass * SMTpass::getInstanceForAbstract() {
	if (instanceforAbstract == NULL)
		instanceforAbstract = new SMTpass();
	return instanceforAbstract;
}

void SMTpass::releaseMemory() {
	if (instance != NULL) {
		delete instance;
		instance = NULL;
	}
	if (instanceforAbstract != NULL) {
		delete instanceforAbstract;
		instanceforAbstract = NULL;
	}
}

SMT_expr SMTpass::getRho(Function &F) {
	if (!rho.count(&F))
		computeRho(F);
	return rho[&F];
}

void SMTpass::reset_SMTcontext() {
	rho.clear();
#if 0
	while (stack_level > 0)
		pop_context();
#else
	switch (getSMTSolver()) {
#ifdef HAS_Z3
		case API_Z3:
			stack_level = 0;
			delete man;
			man = new z3_manager();
			break;
#endif
#ifdef HAS_YICES
		case API_YICES:
			stack_level = 0;
			delete man;
			man = new yices();
			break;
#endif
		default:
			while (stack_level > 0)
				pop_context();
			//man = new SMTlib();
	}
#endif
}

SMT_expr SMTpass::texpr1ToSmt(ap_texpr1_t texpr) {
	// NOT IMPLEMENTED
	SMT_expr NULL_expr;
	return NULL_expr;
}

SMT_expr SMTpass::linexpr1ToSmt(BasicBlock* b, ap_linexpr1_t linexpr, bool &integer, bool &skip) {
	std::vector<SMT_expr> elts;
	SMT_expr val;
	SMT_expr coefficient;
	bool infinity;

	skip = false;
	integer = true;
	double value;
	size_t i;
	ap_var_t var;
	ap_coeff_t* coeff;

	// first, we figure out whether the expression has to be of type real instead of
	// integer
	// we iterate over the terms and find if there is some real variables
	ap_linexpr1_ForeachLinterm1(&linexpr,i,var,coeff){
		if (!((Value*)var)->getType()->isIntegerTy()) {
			// check if the coeff associated to this non-int variable is != 0
			// if it is, then the expression has to be of type real
			coefficient = scalarToSmt(coeff->val.scalar,integer,value,infinity);
			if (infinity) {
				skip = true;
				return man->SMT_mk_num(0);
			}
			if (value != 0) {
				integer = false;
			}
		}
	}

	ap_linexpr1_ForeachLinterm1(&linexpr,i,var,coeff){
		bool primed;
		if (Instruction * I = dyn_cast<Instruction>((Value*)var)) {
			primed = is_primed(b,*I);
		} else {
			primed = false;
		}
		val = getValueExpr((Value*)var, primed);

		if ( !integer && ((Value*)var)->getType()->isIntegerTy()) {
			val = man->SMT_mk_int2real(val);
		}

		coefficient = scalarToSmt(coeff->val.scalar,integer,value,infinity);
		if (infinity) {
			skip = true;
			return man->SMT_mk_num(0);
		}

		switch ((int)value) {
			case 0:
				break;
			case 1:
				elts.push_back(val);
				break;
			default:
				elts.push_back(man->SMT_mk_mul(val,coefficient));
				break;
		}
	}
	coeff = ap_linexpr1_cstref(&linexpr);
	coefficient = scalarToSmt(coeff->val.scalar,integer,value,infinity);
	if (infinity) {
		skip = true;
		return man->SMT_mk_num(0);
	}
	if (value != 0)
		elts.push_back(coefficient);

	return man->SMT_mk_sum(elts);
}

SMT_expr SMTpass::scalarToSmt(ap_scalar_t * scalar, bool integer, double &value, bool &infinity) {
	mp_rnd_t round = GMP_RNDU;
	ap_double_set_scalar(&value,scalar,round);
	if (std::isinf(value)) {
		infinity = true;
		// will be unused, but we must return something
		return man->SMT_mk_real(0);
	}
	infinity = false;
	if (integer) {
		mpq_t mpq;
		mpq_init(mpq);
		ap_mpq_set_scalar(mpq,scalar,round);
		SMT_expr res = man->SMT_mk_num_mpq(mpq);
		mpq_clear(mpq);
		return res;
	} else {
		return man->SMT_mk_real(value);
	}
}

SMT_expr SMTpass::lincons1ToSmt(BasicBlock * b, ap_lincons1_t lincons, bool &skip) {
	ap_constyp_t* constyp = ap_lincons1_constypref(&lincons);
	ap_linexpr1_t linexpr = ap_lincons1_linexpr1ref(&lincons);
	//ap_coeff_t * coeff = ap_lincons1_cstref(&lincons);
	SMT_expr scalar_smt;
	bool integer;
	SMT_expr linexpr_smt = linexpr1ToSmt(b, linexpr, integer, skip);

	if (skip)
		return man->SMT_mk_true();

	if (integer || *constyp == AP_CONS_EQMOD)
		scalar_smt = man-> SMT_mk_int0();
	else
		scalar_smt = man-> SMT_mk_real0();

	switch (*constyp) {
		case AP_CONS_EQ:
			return man->SMT_mk_eq(linexpr_smt,scalar_smt);
		case AP_CONS_SUPEQ:
			return man->SMT_mk_ge(linexpr_smt,scalar_smt);
		case AP_CONS_SUP:
			return man->SMT_mk_gt(linexpr_smt,scalar_smt);
		case AP_CONS_EQMOD:
			{
				double value; // TODO double bof
				bool infinity;
				SMT_expr modulo = scalarToSmt(ap_lincons1_lincons0ref(&lincons)->scalar,true,value,infinity);
				assert(!(value == 0));
				assert(infinity == false);

				if (integer) {
					return man->SMT_mk_divides(modulo, linexpr_smt); // assumes scalar_smt is 0
				} else {
#if SMT_HAS_WORKING_MODULO
					// This segfaults in Z3 3.8
					SMT_expr test = man->SMT_mk_is_int(linexpr_smt);
					if (value == 1) {
						return test;
					} else {
						SMT_expr intexpr = man->SMT_mk_real2int(linexpr_smt);
						std::vector<SMT_expr> args;
						args.push_back(test);
						args.push_back( man->SMT_mk_divides(modulo, intexpr)); // assumes scalar_smt is zero
						return man->SMT_mk_and(args);
					}
#else
					return man->SMT_mk_true();
#endif
				}
			}
		case AP_CONS_DISEQ:
			return man->SMT_mk_diseq(linexpr_smt,scalar_smt);
	}
	// unreachable
	SMT_expr NULL_expr;
	return NULL_expr;
}

SMT_expr SMTpass::tcons1ToSmt(ap_tcons1_t tcons) {
	ap_constyp_t* constyp = ap_tcons1_constypref(&tcons);
	ap_texpr1_t texpr = ap_tcons1_texpr1ref(&tcons);
	ap_scalar_t* scalar = ap_tcons1_scalarref(&tcons);
	bool integer = true;
	double value;
	bool infinity;
	SMT_expr texpr_smt = texpr1ToSmt(texpr);
	SMT_expr scalar_smt = scalarToSmt(scalar,integer,value,infinity);
	if (infinity)
		return man->SMT_mk_num(0);

	switch (*constyp) {
		case AP_CONS_EQ:
			return man->SMT_mk_eq(texpr_smt,scalar_smt);
		case AP_CONS_SUPEQ:
			return man->SMT_mk_ge(texpr_smt,scalar_smt);
		case AP_CONS_SUP:
			return man->SMT_mk_gt(texpr_smt,scalar_smt);
		case AP_CONS_EQMOD:
			// TODO : this is not correct
			return man->SMT_mk_eq(texpr_smt,scalar_smt);
		case AP_CONS_DISEQ:
			return man->SMT_mk_diseq(texpr_smt,scalar_smt);
	}
	// unreachable
	SMT_expr NULL_expr;
	return NULL_expr;
}

SMT_expr SMTpass::AbstractDisjToSmt(BasicBlock * b, AbstractDisj * A, bool insert_booleans) {
	std::vector<SMT_expr> disj;
	int N = A->getMaxIndex();
	if (insert_booleans) {
		std::vector<SMT_expr> D;
		// we create a boolean predicate for each disjunct
		for (int index = 0;index <= N; index++) {
			SMT_var dvar = man->SMT_mk_bool_var(getDisjunctiveIndexName(A,index));
			D.push_back(man->SMT_mk_expr_from_bool_var(dvar));
		}
		for (int index = 0;index <= N; index++) {
			std::vector<SMT_expr> cunj;
			cunj.push_back(AbstractToSmt(b,A->getDisjunct(index)));
			for (int j = 0;j <= N; j++) {
				if (index == j)
					cunj.push_back(D[j]);
				else
					cunj.push_back(man->SMT_mk_not(D[j]));
			}
			disj.push_back(man->SMT_mk_and(cunj));
		}
	} else {
		for (int index = 0;index <= N; index++) {
			disj.push_back(AbstractToSmt(b,A->getDisjunct(index)));
		}
	}
	return man->SMT_mk_or(disj);
}

SMT_expr SMTpass::AbstractToSmt(BasicBlock * b, Abstract * A) {
	if (A->is_bottom()) return man->SMT_mk_false();
	if (AbstractDisj * Adis = dynamic_cast<AbstractDisj*>(A))
		return AbstractDisjToSmt(b,Adis,false);

	std::vector<SMT_expr> constraints;
	ap_lincons1_t lincons;
	ap_lincons1_array_t lincons_array = A->to_lincons_array();
	size_t n = ap_lincons1_array_size(&lincons_array);
	bool skip;
	for (size_t i = 0; i < n; i++) {
		lincons = ap_lincons1_array_get(&lincons_array,i);
		constraints.push_back(lincons1ToSmt(b,lincons,skip));
	}
	ap_lincons1_array_clear(&lincons_array);
	if (constraints.size() == 0)
		return man->SMT_mk_true();
	else
		return man->SMT_mk_and(constraints);
}

const std::string SMTpass::getDisjunctiveIndexName(AbstractDisj * A, int index) {
	std::ostringstream name;
	name << "d_" << A << "_" << index;
	return name.str();
}

const std::string SMTpass::getUndeterministicChoiceName(Value * v) {
	std::ostringstream name;
	name << "c_" << v;
	return name.str();
}

const std::string SMTpass::getNodeSubName(BasicBlock * b) {
	if (NodeNames.count(b) == 0) {
		NodeNames[b] = CurrentNodeName;
		NodeAddress[CurrentNodeName] = b;
		CurrentNodeName++;
	}
	std::ostringstream oss;
	oss << NodeNames[b];
	return oss.str();
}

BasicBlock * SMTpass::getNodeBasicBlock(std::string name) {
	return NodeAddress[boost::lexical_cast<int>(name)];
}

const std::string SMTpass::getNodeName(BasicBlock* b, bool src) {
	std::string name;
	Pr * FPr = Pr::getInstance(b->getParent());
	if (FPr->inPr(b)) {
		if (src)
			name = "bs_";
		else
			name = "bd_";
	} else {
		name = "b_";
	}

	name += getNodeSubName(b);
	return name;
}

const std::string SMTpass::getEdgeName(BasicBlock* b1, BasicBlock* b2) {
	std::string name;
	name =  "t_"
		+ getNodeSubName(b1)
		+ "_"
		+ getNodeSubName(b2);
	return name;
}

const std::string SMTpass::getValueName(Value * v, bool primed) {
	std::string name;
	std::string var = getVarName(v);
	if (primed)
		name = "x_prime_" + var + "_";
	else
		name = "x_" + var + "_";
	return name;
}

std::map<Value*,std::string> SMTpass::VarNames;
int VarNames_unnamed = 0;

const std::string SMTpass::getVarName(Value * v) {
	if (VarNames.count(v) && !isa<UndefValue>(v)) {
		return VarNames[v];
	}

	std::string s_string;
	raw_string_ostream * s = new raw_string_ostream(s_string);

	if (v->hasName()) {
		*s << v->getName();
	} else {
		VarNames_unnamed++;
		//*s << *v;
		*s << "unnamed_" << VarNames_unnamed;
	}

	std::string & name = s->str();
	//*Dbg << name << "\n";
	size_t found;
	found=name.find_first_of("%");
	if (found!=std::string::npos) {
		name = name.substr(found);
	}
	found=name.find_first_of(" ");
	if (found!=std::string::npos) {
		name.resize(found);
	}
	delete s;
	if (!isa<UndefValue>(v))
		VarNames[v] = name;
	return name;
}

SMT_type SMTpass::getValueType(Value * v) {
	switch (v->getType()->getTypeID()) {
		case Type::PointerTyID:
			if (!pointer_arithmetic()) {assert(false && "this code should be unreachable");};
			// no break is done on purpose, since pointer == integer in this case
		case Type::IntegerTyID:
			if (v->getType()->getPrimitiveSizeInBits() == 1) {
				return man->bool_type;
			} else {
				return man->int_type;
			}
		default:
			return man->float_type;
	}
}

SMT_var SMTpass::getVar(Value * v, bool primed) {
	std::string name = getValueName(v,primed);
	return man->SMT_mk_var(name,getValueType(v));
}

SMT_var SMTpass::getBoolVar(Value * v, bool primed) {
	std::string name = getValueName(v,primed);
	return man->SMT_mk_bool_var(name);
}

SMT_expr SMTpass::getValueExpr(Value * v, bool primed) {
	SMT_var var;
	SMT_expr NULL_res;

	ap_texpr_rtype_t ap_type;
	switch (Expr::get_ap_type(v, ap_type)) {
		case 0: // this is an integer or float
			if (isa<ConstantInt>(v)) {
				ConstantInt * Int = dyn_cast<ConstantInt>(v);
				int n = Int->getSExtValue();
				return man->SMT_mk_num((int)n);
			} else if (isa<ConstantPointerNull>(v)) {
				return man->SMT_mk_num((int)0);
			} else if (isa<ConstantFP>(v)) {
				ConstantFP * FP = dyn_cast<ConstantFP>(v);
				APFloat APF = FP->getValueAPF();
				double x;
				if (FP->getType()->isFloatTy()) {
					x = (double)APF.convertToFloat();
				} else if (FP->getType()->isDoubleTy()) {
					x = APF.convertToDouble();
				}
				if (std::isnan(x) || std::isinf(x)) {
					// x is not a number or is infinity...
					std::ostringstream name;
					name << getValueName(v,false);
					nundef++;
					return man->SMT_mk_expr_from_var(man->SMT_mk_var(name.str(),getValueType(v)));
				}
				return man->SMT_mk_real(x);
			} else if (isa<UndefValue>(v)) {
				std::ostringstream name;
				//name << getValueName(v,false) << "_" << nundef;
				name << getValueName(v,false);
				nundef++;
				return man->SMT_mk_expr_from_var(man->SMT_mk_var(name.str(),getValueType(v)));
			} else if (isa<Instruction>(v) || isa<Argument>(v)) {
				var = getVar(v,primed);
				return man->SMT_mk_expr_from_var(var);
			} else {
				var = getVar(v,primed);
				return man->SMT_mk_expr_from_var(var);
			}
		case 2: // this is a Boolean
			{
				SMT_expr cond;
				if (isa<Instruction>(v) || isa<Argument>(v)) {
					var = getBoolVar(v,primed);
					return man->SMT_mk_expr_from_bool_var(var);
				} else if (ConstantInt * vint = dyn_cast<ConstantInt>(v)) {
					// the constant is either true or false
					if (vint->isZero()) {
						cond = man->SMT_mk_false();
					} else if (vint->isOne()) {
						cond = man->SMT_mk_true();
					}
				}
				if (cond.is_empty()) {
					SMT_var cvar = man->SMT_mk_bool_var(getUndeterministicChoiceName(v));
					cond = man->SMT_mk_expr_from_bool_var(cvar);
				}
				return cond;
			}
		case 1: // this value is not of interesting type
			*Dbg << "ERROR: getValueExpr returns NULL for the following value:\n" << *v << "\n";
			assert(false && "ERROR: getValueExpr returns NULL");
	}
	return NULL_res;
}

void SMTpass::getElementFromString(
		std::string name,
		bool &isEdge,
		bool &isIndex,
		bool &start,
		BasicBlock * &src,
		BasicBlock * &dest,
		int &index) {

	std::string edge ("t_");
	std::string simple_node ("b_");
	std::string source_node ("bs_");
	std::string dest_node ("bd_");
	std::string disjunctive_index ("d_");
	size_t found;
	void* address;
	src = NULL;
	dest = NULL;
	start = false;

	vector<std::string> fields;
	split( fields, name, is_any_of( "_" ) );

	// case 1 : this is an edge
	found = name.substr(0,edge.size()).find(edge);
	if (found!=std::string::npos) {
		isEdge = true;
		src = getNodeBasicBlock(fields[1]);
		dest = getNodeBasicBlock(fields[2]);
		return;
	}
	isEdge = false;

	// case 2 : this is a disjunctive index
	found = name.substr(0,disjunctive_index.size()).find(disjunctive_index);
	if (found!=std::string::npos) {
		isIndex = true;
		std::string source = name.substr (2,9);
		std::string stringindex(name);
		stringindex.erase(0,12);
		std::istringstream indexStream(stringindex);
		indexStream >> index;
		return;
	}
	isIndex = false;

	std::string nodename;
	// case 3 : this is a node
	found = name.substr(0,simple_node.size()).find(simple_node);
	if (found!=std::string::npos) {
		// this is a node of the form b_*
		src = getNodeBasicBlock(fields[1]);
	} else {
		found = name.substr(0,source_node.size()).find(source_node);
		if (found==std::string::npos) found = name.find(dest_node);
		else start = true;
		if (found!=std::string::npos) {
			src = getNodeBasicBlock(fields[1]);
		}
	}
}

void SMTpass::computePrSuccAndPred(
		Function &F
		) {
	Pr * FPr = Pr::getInstance(&F);
	std::set<BasicBlock*>::iterator i = FPr->getPr()->begin(), e = FPr->getPr()->end();
	for (;i!= e; ++i) {
		int counter = 0;
		BasicBlock * dest = *i;
		std::set<BasicBlock*> visited;
		std::queue<BasicBlock*> ToBeComputed;
		ToBeComputed.push(dest);
		while (!ToBeComputed.empty()) {
			counter++;
			BasicBlock * b = ToBeComputed.front();
			ToBeComputed.pop();
			if (visited.count(b)) continue;
			visited.insert(b);
			if (!FPr->inPr(b) || b == dest) {
				BasicBlock * pred;
				for (pred_iterator p = pred_begin(b), E = pred_end(b); p != E; ++p) {
					pred = *p;
					if (!FPr->inPr(pred)) {
						if (!visited.count(pred)) {
							ToBeComputed.push(pred);
						}
					} else {
						FPr->Pr_pred[dest].insert(pred);
						FPr->Pr_succ[pred].insert(dest);
					}
				}
			}
		}
	}
}

void SMTpass::computeRhoRec(Function &F,
		std::set<BasicBlock*> * visited,
		BasicBlock * dest) {
	Pr * FPr = Pr::getInstance(&F);

	std::queue<BasicBlock*> ToBeComputed;
	ToBeComputed.push(dest);

	while (!ToBeComputed.empty()) {
		BasicBlock * b = ToBeComputed.front();
		ToBeComputed.pop();
		bool first = (visited->count(b) > 0);
		visited->insert(b);
		if (first) continue;

		if (!FPr->inPr(b) || b == dest) {
			// we recursively construct Rho, starting from the predecessors
			BasicBlock * pred;
			for (pred_iterator p = pred_begin(b), E = pred_end(b); p != E; ++p) {
				pred = *p;
				if (!FPr->inPr(pred)) {
					if (!visited->count(pred)) {
						ToBeComputed.push(pred);
					}
				}
			}
		}

		if (first) continue;

		// we create a boolean reachability predicate for the basicblock
		SMT_var bvar = man->SMT_mk_bool_var(getNodeName(b,false));
		// we associate it the right predicate depending on its incoming edges
		std::vector<SMT_expr> predicate;
		for (pred_iterator p = pred_begin(b), e = pred_end(b); p != e; ++p) {
			SMT_var evar = man->SMT_mk_bool_var(getEdgeName(*p,b));
			predicate.push_back(man->SMT_mk_expr_from_bool_var(evar));
		}
		SMT_expr bpredicate;

		if (predicate.size() != 0)
			bpredicate = man->SMT_mk_or(predicate);
		else
			bpredicate = man->SMT_mk_false();

		SMT_expr bvar_exp = man->SMT_mk_expr_from_bool_var(bvar);
		bvar_exp = man->SMT_mk_eq(bvar_exp,bpredicate);
		rho_components.push_back(bvar_exp);
		// we compute the transformation due to the basicblock's
		// instructions
		bvar = man->SMT_mk_bool_var(getNodeName(b,false));
		instructions.clear();

		for (BasicBlock::iterator i = b->begin(), e = b->end();
				i != e; ++i) {
			visit(*i);
		}

		if (instructions.size() > 0) {
			bpredicate = man->SMT_mk_and(instructions);
			instructions.clear();
			SMT_expr bvar_exp = man->SMT_mk_expr_from_bool_var(bvar);
			bvar_exp = man->SMT_mk_not(bvar_exp);
			std::vector<SMT_expr> implies;
			implies.push_back(bvar_exp);
			implies.push_back(bpredicate);
			bvar_exp = man->SMT_mk_or(implies);
			rho_components.push_back(bvar_exp);
		}
	}
}

void SMTpass::computeRho(Function &F) {
	BasicBlock * b;
	Pr * FPr = Pr::getInstance(&F);

	rho_components.clear();
	std::set<BasicBlock*>::iterator i = FPr->getPr()->begin(), e = FPr->getPr()->end();
	std::set<BasicBlock*> visited;
	for (;i!= e; ++i) {
		b = *i;
		computeRhoRec(F,&visited,b);
	}
	rho[&F] = man->SMT_mk_and(rho_components);
	rho_components.clear();
	computePrSuccAndPred(F);
}

void SMTpass::push_context() {
	stack_level++;
	man->push_context();
}

void SMTpass::pop_context() {
	stack_level--;
	man->pop_context();
}

void SMTpass::SMT_assert(SMT_expr expr) {
	man->SMT_assert(expr);
}

SMT_expr SMTpass::createSMTformula(
		BasicBlock * source,
		bool use_X_d,
		params t,
		SMT_expr constraint) {
	Function &F = *source->getParent();
	Pr * FPr = Pr::getInstance(&F);
	std::vector<SMT_expr> formula;
	//formula.push_back(getRho(F));

	SMT_var bvar = man->SMT_mk_bool_var(getNodeName(source,true));
	formula.push_back(man->SMT_mk_expr_from_bool_var(bvar));

	std::set<BasicBlock*>::iterator i = FPr->getPr()->begin(), e = FPr->getPr()->end();
	for (; i!=e; ++i) {
		if (*i != source) {
			bvar = man->SMT_mk_bool_var(getNodeName(*i,true));
			formula.push_back(man->SMT_mk_not(man->SMT_mk_expr_from_bool_var(bvar)));
		}
	}

	Abstract * A = Nodes[source]->X_s[t];
	if (AbstractDisj * Adis = dynamic_cast<AbstractDisj*>(A))
		formula.push_back(AbstractDisjToSmt(NULL,Adis,true));
	else
		formula.push_back(AbstractToSmt(NULL,A));

	std::vector<SMT_expr> Or;
	std::set<BasicBlock*> Successors = FPr->getPrSuccessors(source);
	i = Successors.begin(), e = Successors.end();
	for (; i!=e; ++i) {
		std::vector<SMT_expr> SuccExp;
		SMT_var succvar = man->SMT_mk_bool_var(getNodeName(*i,false));
		SuccExp.push_back(man->SMT_mk_expr_from_bool_var(succvar));

		if (use_X_d)
			SuccExp.push_back(man->SMT_mk_not(AbstractToSmt(*i,Nodes[*i]->X_d[t])));
		else {
			SuccExp.push_back(man->SMT_mk_not(AbstractToSmt(*i,Nodes[*i]->X_s[t])));
		}

		Or.push_back(man->SMT_mk_and(SuccExp));
	}
	// if Or is empty, that means the block has no successor => formula has to
	// be false
	if (Or.size() > 0)
		formula.push_back(man->SMT_mk_or(Or));
	else
		formula.push_back(man->SMT_mk_false());

	// if constraint argument is specified, we insert it into our formula
	if (!constraint.is_empty())
		formula.push_back(constraint);

	return man->SMT_mk_and(formula);
}

int SMTpass::SMTsolve(
		SMT_expr expr,
		std::list<BasicBlock*> * path,
		Function * F,
		params passID) {
	int index;
	return SMTsolve(expr,path,index,F,passID);
}

int SMTpass::SMTsolve(
		SMT_expr expr,
		std::list<BasicBlock*> * path,
		int &index,
		Function * F,
		params passID) {
	std::set<std::string> true_booleans;
	std::map<BasicBlock*, BasicBlock*> succ;
	int res;

	sys::TimeValue time(sys::TimeValue::now());

	res = man->SMT_check(expr,&true_booleans);

	if (Total_time_SMT[passID].count(F) == 0) {
		sys::TimeValue * time_SMT = new sys::TimeValue(0,0);
		Total_time_SMT[passID][F] = time_SMT;
	}

	*Total_time_SMT[passID][F] += sys::TimeValue::now()-time;

	if (res != 1) return res;
	bool isEdge, isIndex, start;
	BasicBlock * src;
	BasicBlock * dest;
	int disj;

	std::set<std::string>::iterator i = true_booleans.begin(), e = true_booleans.end();
	for (; i != e; ++i) {
		std::string name = *i;
		getElementFromString(name,isEdge,isIndex,start,src,dest,disj);
		if (isEdge) {
			succ[src] = dest;
		} else if (isIndex) {
			index = disj;
		} else {
			if (start)
				path->push_back(src);
		}
		Out->flush();
	}

	while (succ.count(path->back())) {
		path->push_back(succ[path->back()]);
		if (path->back() == path->front()) break;
	}
	return res;
}

int SMTpass::SMTsolve_simple(SMT_expr expr) {
	std::set<std::string> true_booleans;
	return man->SMT_check(expr,&true_booleans);
}

void SMTpass::visitReturnInst (ReturnInst &I) {
}

void SMTpass::visitBranchInst (BranchInst &I) {
	BasicBlock * b = I.getParent();

	SMT_var bvar = man->SMT_mk_bool_var(getNodeName(b,true));
	SMT_expr bexpr = man->SMT_mk_expr_from_bool_var(bvar);
	BasicBlock * s = I.getSuccessor(0);
	SMT_var evar = man->SMT_mk_bool_var(getEdgeName(b,s));
	SMT_expr eexpr = man->SMT_mk_expr_from_bool_var(evar);

	if (I.isUnconditional() || s == I.getSuccessor(1)) {
		rho_components.push_back(man->SMT_mk_eq(eexpr,bexpr));
	} else {
		SMT_expr components_and;
		std::vector<SMT_expr> components;

		SMT_expr cond = getValueExpr(I.getOperand(0),false);

		components.push_back(bexpr);
		if (!cond.is_empty())
			components.push_back(cond);
		components_and = man->SMT_mk_and(components);
		rho_components.push_back(man->SMT_mk_eq(eexpr,components_and));

		components.clear();
		cond = man->SMT_mk_not(cond);
		s = I.getSuccessor(1);
		evar = man->SMT_mk_bool_var(getEdgeName(b,s));
		eexpr = man->SMT_mk_expr_from_bool_var(evar);
		components.push_back(bexpr);
		if (!cond.is_empty())
			components.push_back(cond);
		components_and = man->SMT_mk_and(components);
		components.clear();
		rho_components.push_back(man->SMT_mk_eq(eexpr,components_and));
	}
}

void SMTpass::visitSwitchInst (SwitchInst &I) {
}

void SMTpass::visitIndirectBrInst (IndirectBrInst &I) {
}

void SMTpass::visitInvokeInst (InvokeInst &I) {
	BasicBlock * b = I.getParent();

	SMT_var cvar = man->SMT_mk_bool_var(getUndeterministicChoiceName(&I));
	SMT_expr cexpr = man->SMT_mk_expr_from_bool_var(cvar);

	SMT_var bvar = man->SMT_mk_bool_var(getNodeName(b,true));
	SMT_expr bexpr = man->SMT_mk_expr_from_bool_var(bvar);

	// normal destination
	BasicBlock * s = I.getNormalDest();
	SMT_var evar = man->SMT_mk_bool_var(getEdgeName(b,s));
	SMT_expr eexpr = man->SMT_mk_expr_from_bool_var(evar);

	std::vector<SMT_expr> components;
	components.push_back(bexpr);
	components.push_back(cexpr);
	rho_components.push_back(man->SMT_mk_eq(eexpr,man->SMT_mk_and(components)));

	// Unwind destination
	s = I.getUnwindDest();
	evar = man->SMT_mk_bool_var(getEdgeName(b,s));
	eexpr = man->SMT_mk_expr_from_bool_var(evar);
	components.pop_back();
	components.push_back(man->SMT_mk_not(cexpr));
	rho_components.push_back(man->SMT_mk_eq(eexpr,man->SMT_mk_and(components)));
}

void SMTpass::visitUnreachableInst (UnreachableInst &I) {
}

void SMTpass::visitAllocaInst (AllocaInst &I) {
}

void SMTpass::visitLoadInst (LoadInst &I) {
}

void SMTpass::visitStoreInst (StoreInst &I) {
}

void SMTpass::visitGetElementPtrInst (GetElementPtrInst &I) {
	if (pointer_arithmetic()) {
		const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
		const SMT_expr op0 = getValueExpr(I.getOperand(0), false);
		const SMT_expr op1 = getValueExpr(I.getOperand(1), false);
		const PointerType* ptrType = static_cast<PointerType*>(I.getPointerOperandType());
		Type* type = ptrType -> getElementType();

		const BasicBlock *bb = I.getParent();
		const Function *fn = bb->getParent();
		const Module *mod = fn->getParent();
		const DataLayout layout(mod);
		const ::uint64_t size=layout.getTypeAllocSize(type);

		// TODO check bit width
		SMT_expr prod = man->SMT_mk_mul(man->SMT_mk_num(size), op1);
		SMT_expr assign = man->SMT_mk_sum(op0, prod);

		rho_components.push_back(man->SMT_mk_eq(expr,assign));
	}
}

SMT_expr SMTpass::construct_phi_ite(PHINode &I, unsigned i, unsigned n) {
	if (i == n-1) {
		// this is the last possible value of the PHI-variable
		return getValueExpr(I.getIncomingValue(i), false);
	}
	SMT_expr incomingVal =	getValueExpr(I.getIncomingValue(i), false);

	// incomingVal is empty if the i-th argument in the PHI is not a number
	// (nan)
	if (incomingVal.is_empty()) return incomingVal;

	SMT_var evar = man->SMT_mk_bool_var(getEdgeName(I.getIncomingBlock(i),I.getParent()));
	SMT_expr incomingBlock = man->SMT_mk_expr_from_bool_var(evar);

	SMT_expr tail = construct_phi_ite(I,i+1,n);

	// tail may also be empty if one operand of the phi is nan
	if (tail.is_empty()) return tail;

	return man->SMT_mk_ite(incomingBlock,incomingVal,tail);
}

bool SMTpass::is_primed(BasicBlock * b, Instruction &I) {
	if (b == NULL) return false;
	Function * F = b->getParent();
	Pr * FPr = Pr::getInstance(F);
	return (b != NULL
			&& (I.getParent() == b && FPr->inPr(b))
			&& isa<PHINode>(I)
	       );
}

void SMTpass::visitPHINode (PHINode &I) {
	ap_texpr_rtype_t ap_type;
	if (Expr::get_ap_type((Value*)&I, ap_type) == 1) return;

	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr assign = construct_phi_ite(I,0,I.getNumIncomingValues());

	if (assign.is_empty()) return;

	//if (is_primed(I.getParent(),I) && I.getNumIncomingValues() != 1) {
	if (I.getNumIncomingValues() != 1) {
		SMT_var bvar = man->SMT_mk_bool_var(getNodeName(I.getParent(),false));
		SMT_expr bexpr = man->SMT_mk_not(man->SMT_mk_expr_from_bool_var(bvar));
		std::vector<SMT_expr> disj;
		disj.push_back(bexpr);
		disj.push_back(man->SMT_mk_eq(expr,assign));
		expr = man->SMT_mk_or(disj);
	} else {
		expr = man->SMT_mk_eq(expr,assign);
	}
	rho_components.push_back(expr);
}

void SMTpass::visitTruncInst (TruncInst &I) {
	if(I.getSrcTy()->isIntegerTy() && I.getDestTy()->isIntegerTy(1)) {
		// we cast an integer to boolean
		SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
		SMT_expr zero = man->SMT_mk_num(0);
		SMT_expr cmp = man->SMT_mk_eq(operand0,zero);

		SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
		rho_components.push_back(man->SMT_mk_eq(expr,cmp));
	}
#ifdef NAIVE_TRUNC
	else {
		const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
		const SMT_expr assign = getValueExpr(I.getOperand(0), false);
		rho_components.push_back(man->SMT_mk_eq(expr,assign));
	}
#endif
}

void SMTpass::visitZExtInst (ZExtInst &I) {
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
	if(I.getSrcTy()->isIntegerTy(1) && I.getDestTy()->isIntegerTy()) {
		// we cast a boolean to an integer
		SMT_expr zero = man->SMT_mk_num(0);
		SMT_expr one = man->SMT_mk_num(1);
		SMT_expr ite = man->SMT_mk_ite(operand0,one,zero);
		rho_components.push_back(man->SMT_mk_eq(expr,ite));
	} else if(I.getSrcTy()->isIntegerTy() && I.getDestTy()->isIntegerTy()) {
		// we cast an integer to an integer of different size
		// we assume the two integers are equal
		// TODO: not sound
		rho_components.push_back(man->SMT_mk_eq(operand0,expr));
	}
}

void SMTpass::visitSExtInst (SExtInst &I) {
#ifdef NAIVE_TRUNC
	const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	const SMT_expr assign = getValueExpr(I.getOperand(0), false);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
#endif
}

void SMTpass::visitFPTruncInst (FPTruncInst &I) {
#ifdef NAIVE_TRUNC
	const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	const SMT_expr assign = getValueExpr(I.getOperand(0), false);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
#endif
}

void SMTpass::visitFPExtInst (FPExtInst &I) {
#ifdef NAIVE_TRUNC
	const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	const SMT_expr assign = getValueExpr(I.getOperand(0), false);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
#endif
}

void SMTpass::visitFPToUIInst (FPToUIInst &I) {
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
	SMT_expr assign = man->SMT_mk_real2int(operand0);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
}

void SMTpass::visitFPToSIInst (FPToSIInst &I) {
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
	SMT_expr assign = man->SMT_mk_real2int(operand0);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
}

void SMTpass::visitUIToFPInst (UIToFPInst &I) {
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
	SMT_expr assign = man->SMT_mk_int2real(operand0);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
}

void SMTpass::visitSIToFPInst (SIToFPInst &I) {
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
	SMT_expr assign = man->SMT_mk_int2real(operand0);
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
}

void SMTpass::visitPtrToIntInst (PtrToIntInst &I) {
	if (pointer_arithmetic()) {
		const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
		const SMT_expr assign = getValueExpr(I.getOperand(0), false);
		rho_components.push_back(man->SMT_mk_eq(expr,assign));
	}
}

void SMTpass::visitIntToPtrInst (IntToPtrInst &I) {
	if (pointer_arithmetic()) {
		const SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
		const SMT_expr assign = getValueExpr(I.getOperand(0), false);
		rho_components.push_back(man->SMT_mk_eq(expr,assign));
	}
}

void SMTpass::visitBitCastInst (BitCastInst &I) {
}

void SMTpass::visitSelectInst (SelectInst &I) {
	// if not interesting type, skip
	ap_texpr_rtype_t ap_type;
	if (Expr::get_ap_type((Value*)&I, ap_type) == 1) return;

	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr TrueValue = getValueExpr(I.getTrueValue(), false);
	SMT_expr FalseValue = getValueExpr(I.getFalseValue(), false);
	SMT_expr cond = getValueExpr(I.getCondition(), false);
	rho_components.push_back(man->SMT_mk_eq(
				expr,
				man->SMT_mk_ite(cond,TrueValue,FalseValue)
				));
}

void SMTpass::visitCallInst(CallInst &I) {
}

void SMTpass::visitVAArgInst (VAArgInst &I) {
}

void SMTpass::visitExtractElementInst (ExtractElementInst &I) {
}

void SMTpass::visitInsertElementInst (InsertElementInst &I) {
}

void SMTpass::visitShuffleVectorInst (ShuffleVectorInst &I) {
}

void SMTpass::visitExtractValueInst (ExtractValueInst &I) {
}

void SMTpass::visitInsertValueInst (InsertValueInst &I) {
}

void SMTpass::visitTerminatorInst (TerminatorInst &I) {
}

void SMTpass::visitBinaryOperator (BinaryOperator &I) {
	bool skip = false;
	ap_texpr_rtype_t ap_type;
	int t = Expr::get_ap_type((Value*)&I, ap_type);

	//primed[I.getParent()].insert(&I);
	//exist_prime.insert(&I);
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr assign;
	SMT_expr operand0 = getValueExpr(I.getOperand(0), false);
	SMT_expr operand1 = getValueExpr(I.getOperand(1), false);

	// if an operand is nan, return...
	if (operand0.is_empty()) return;
	if (operand1.is_empty()) return;

	switch(I.getOpcode()) {
		// Standard binary operators...
		case Instruction::Add :
		case Instruction::FAdd:
			assign = man->SMT_mk_sum(operand0,operand1);
			break;
		case Instruction::Sub :
		case Instruction::FSub:
			assign = man->SMT_mk_sub(operand0,operand1);
			break;
		case Instruction::Mul :
		case Instruction::FMul:
			if (!skipNonLinear())
				assign = man->SMT_mk_mul(operand0,operand1);
			break;
		case Instruction::And :
			//if (!t) return;
			if (t != 2) return;
			{
				std::vector<SMT_expr> operands;
				operands.push_back(operand0);
				operands.push_back(operand1);
				assign = man->SMT_mk_and(operands);
			}
			break;
		case Instruction::Or  :
			//if (!t) return;
			if (t != 2) return;
			{
				std::vector<SMT_expr> operands;
				operands.push_back(operand0);
				operands.push_back(operand1);
				assign = man->SMT_mk_or(operands);
			}
			break;
		case Instruction::Xor :
			//if (!t) return;
			if (t != 2) return;
			assign = man->SMT_mk_xor(operand0,operand1);
			break;
		case Instruction::UDiv:
		case Instruction::SDiv:
			if (!skipNonLinear())
				assign = man->SMT_mk_div(operand0,operand1);
			break;
		case Instruction::FDiv:
			if (!skipNonLinear())
				assign = man->SMT_mk_div(operand0,operand1,false);
			break;
		case Instruction::URem:
		case Instruction::SRem:
		case Instruction::FRem:
			//skip = true;
			if (!skipNonLinear())
				assign = man->SMT_mk_rem(operand0,operand1);
			break;
			// the others are not implemented
		case Instruction::Shl : // Shift left  (logical)
		case Instruction::LShr: // Shift right (logical)
		case Instruction::AShr: // Shift right (arithmetic)
		case Instruction::BinaryOpsEnd:
			// NOT IMPLEMENTED
			return;
	}
	if (!assign.is_empty())
		rho_components.push_back(man->SMT_mk_eq(expr,assign));
}

void SMTpass::visitCmpInst (CmpInst &I) {
	SMT_expr expr = getValueExpr(&I, is_primed(I.getParent(),I));
	SMT_expr assign;

	ap_texpr_rtype_t ap_type;
	if (Expr::get_ap_type((Value*)I.getOperand(0), ap_type)) {
		// the comparison is not between integers or reals
		// we create an undeterministic choice variable
		SMT_var cvar = man->SMT_mk_bool_var(getUndeterministicChoiceName(&I));
		assign = man->SMT_mk_expr_from_bool_var(cvar);
		rho_components.push_back(man->SMT_mk_eq(expr,assign));
		return;
	}

	SMT_expr op1, op2;

	op1 = getValueExpr(I.getOperand(0), false);
	op2 = getValueExpr(I.getOperand(1), false);

	if (op1.is_empty() || op2.is_empty()) {
		// the comparison involve at least an operand that is nan
		SMT_var cvar = man->SMT_mk_bool_var(getUndeterministicChoiceName(&I));
		assign = man->SMT_mk_expr_from_bool_var(cvar);
		rho_components.push_back(man->SMT_mk_eq(expr,assign));
		return;
	}

	switch (I.getPredicate()) {
		case CmpInst::FCMP_FALSE:
			assign = man->SMT_mk_false();
			break;
		case CmpInst::FCMP_TRUE:
			assign = man->SMT_mk_true();
			break;
		case CmpInst::FCMP_OEQ:
		case CmpInst::FCMP_UEQ:
		case CmpInst::ICMP_EQ:
			assign = man->SMT_mk_eq(op1,op2);
			break;
		case CmpInst::FCMP_OGT:
		case CmpInst::FCMP_UGT:
		case CmpInst::ICMP_UGT:
		case CmpInst::ICMP_SGT:
			assign = man->SMT_mk_gt(op1,op2);
			break;
		case CmpInst::FCMP_OLT:
		case CmpInst::FCMP_ULT:
		case CmpInst::ICMP_ULT:
		case CmpInst::ICMP_SLT:
			assign = man->SMT_mk_lt(op1,op2);
			break;
		case CmpInst::FCMP_OGE:
		case CmpInst::FCMP_UGE:
		case CmpInst::ICMP_UGE:
		case CmpInst::ICMP_SGE:
			assign = man->SMT_mk_ge(op1,op2);
			break;
		case CmpInst::FCMP_OLE:
		case CmpInst::FCMP_ULE:
		case CmpInst::ICMP_ULE:
		case CmpInst::ICMP_SLE:
			assign = man->SMT_mk_le(op1,op2);
			break;
		case CmpInst::FCMP_ONE:
		case CmpInst::FCMP_UNE:
		case CmpInst::ICMP_NE:
			assign = man->SMT_mk_diseq(op1,op2);
			break;
		case CmpInst::FCMP_ORD:
		case CmpInst::FCMP_UNO:
		case CmpInst::BAD_ICMP_PREDICATE:
		case CmpInst::BAD_FCMP_PREDICATE:
			{
				SMT_var cvar = man->SMT_mk_bool_var(getUndeterministicChoiceName(&I));
				assign = man->SMT_mk_expr_from_bool_var(cvar);
			}
			break;
	}
	rho_components.push_back(man->SMT_mk_eq(expr,assign));
}

void SMTpass::visitCastInst (CastInst &I) {
}


/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once

#include <eos/chain/types.hpp>
#include <eos/chain/multi_index_includes.hpp>

#include <eos/types/types.hpp>

#include <eos/utilities/exception_macros.hpp>

#include <chainbase/chainbase.hpp>

#include <boost/multi_index/mem_fun.hpp>

namespace eosio {
namespace chain {

FC_DECLARE_EXCEPTION(producer_race_overflow_exception, 10000000, "Producer Virtual Race time has overflowed");

/**
 * @brief The producer_votes_object class tracks all votes for and by the block producers
 *
 * This class tracks the voting for block producers, as well as the virtual time 'race' to select the runner-up block
 * producer.
 *
 * This class also tracks the votes cast by block producers on various chain configuration options and key documents.
 */
class producer_votes_object : public chainbase::object<producer_votes_object_type, producer_votes_object> {
   OBJECT_CTOR(producer_votes_object)

   id_type id;
   types::account_name ownerName;

   /**
    * @brief Update the tally of votes for the producer, while maintaining virtual time accounting
    *
    * This function will update the producer's position in the race
    *
    * @param deltaVotes The change in votes since the last update
    * @param current_race_time The current "race time"
    */
   void update_votes(types::share_type deltaVotes, types::uint128 current_race_time);
   /// @brief Get the number of votes this producer has received
   types::share_type get_votes() const { return race.speed; }
   pair<types::share_type,id_type> get_vote_order()const { return { race.speed, id }; }

   /**
    * These fields are used for the producer scheduling algorithm which uses a virtual race to ensure that runner-up
    * producers are given proportional time for producing blocks. Producers are constantly running a racetrack of
    * length config::producer_race_lap_length, at a speed equal to the number of votes they have received. Runner-up
    * producers, who lack sufficient votes to get in as a top-N voted producer, get scheduled to produce a block every
    * time they finish the race. The race algorithm ensures that runner-up producers are scheduled with a frequency
    * proportional to their relative vote tallies; i.e. a runner-up with more votes is scheduled more often than one
    * with fewer votes, but all runners-up with any votes will theoretically be scheduled eventually.
    *
    * Whenever a runner-up producer needs to be scheduled, the one projected to finish the race next is taken, with the
    * caveat that any producers already scheduled for other reasons (such as being a top-N voted producer) cannot be
    * scheduled a second time in the same round, and thus they are skipped/ignored when looking for the producer
    * projected to finish next. After selecting the runner-up for scheduling, the global current race time is updated
    * to the point at which the winner crossed the finish line (i.e. his position is zero: he is now beginning his next
    * lap). Furthermore, all producers which crossed the finish line ahead of the winner, but were ineligible to win as
    * they were already scheduled in the next round, are effectively held at the finish line until the winner crosses,
    * so their positions are all also set to zero.
    *
    * Every time the number of votes for a given producer changes, the producer's speed in the race changes and thus
    * his projected finishing time changes. In this event, the stats must be updated: first, the producer's position is
    * updated to reflect his progress since the last stats update to the current race time; second, the producer's
    * projected finishing time based on his new position and speed are updated so we know where he shakes out in the
    * new projected finish times.
    *
    * @warning Do not update these values directly; use @ref update_votes instead!
    */
   struct {
      /// The current speed for this producer (which is actually the total votes for the producer)
      types::share_type speed = 0;
      /// The position of this producer when we last updated the records
      types::uint128 position = 0;
      /// The "race time" when we last updated the records
      types::uint128 position_update_time = 0;
      /// The projected "race time" at which this producer will finish the race
      types::uint128 projected_finish_time = std::numeric_limits<types::uint128>::max();

      /// Set all fields on race, given the current speed, position, and time
      void update(types::share_type currentSpeed, types::uint128 current_position, types::uint128 current_race_time) {
         speed = currentSpeed;
         position = current_position;
         position_update_time = current_race_time;
         auto distance_remaining = config::producer_race_lap_length - position;
         auto projected_time_to_finish = speed > 0? distance_remaining / speed
                                               : std::numeric_limits<types::uint128>::max();
         EOS_ASSERT(current_race_time <= std::numeric_limits<types::uint128>::max() - projected_time_to_finish,
                    producer_race_overflow_exception, "Producer race time has overflowed",
                    ("currentTime", current_race_time)("timeToFinish", projected_time_to_finish)("limit", std::numeric_limits<types::uint128>::max()));
         projected_finish_time = current_race_time + projected_time_to_finish;
      }
   } race;

   void start_new_race_lap(types::uint128 current_race_time) { race.update(race.speed, 0, current_race_time); }
   types::uint128 projected_race_finish_time() const { return race.projected_finish_time; }

   typedef std::pair<types::uint128,id_type> rft_order_type;
   rft_order_type projected_race_finish_time_order() const { return {race.projected_finish_time,id}; }
};

/**
 * @brief The proxy_vote_object tracks proxied votes
 *
 * This object is created when an account indicates that it wishes to allow other accounts to proxy their votes to it,
 * so that the proxying accounts add their stake to the proxy target's votes.
 *
 * When an account S proxies its votes to an account P, we look up the @ref proxy_vote_object with @ref proxy_target P,
 * and add S to the @ref proxy_sources, and add S's stake to @proxied_stake. We then update @ref proxy_target's votes, to
 * account for the change in its voting weight. Any time S's stake changes, we update the @ref proxied_stake accordingly.
 * If S terminates its vote delegation to P, we remove S and its stake from @ref proxySources and @ref proxiedStake,
 * then update @ref proxy_target's votes. If P stops accepting proxied votes, then its @ref proxy_vote_object is removed
 * and all accounts listed in @ref proxy_sources revert back to an unproxied voting state.
 *
 * Whenever any account A changes its votes, we check if there is some @ref ProxyVoteObject for which A is the target,
 * and if so, we add the @ref proxiedStake to its voting weight.
 *
 * An account A may only proxy to one account at a time, and if A has proxied its votes to some other account, A may
 * not cast any other votes until it unproxies its voting power.
 */
class proxy_vote_object : public chainbase::object<proxy_vote_object_type, proxy_vote_object> {
   OBJECT_CTOR(proxy_vote_object, (proxy_sources))

   id_type id;
   /// The account receiving the proxied voting power
   types::account_name proxy_target;
   /// The list of accounts proxying their voting power to @ref proxy_target
   shared_set<types::account_name> proxy_sources;
   /// The total stake proxied to @ref proxy_target. At all times, this should be equal to the sum of stake over all
   /// accounts in @ref proxy_sources
   types::share_type proxied_stake = 0;

   void add_proxy_source(const types::account_name& source, share_type source_stake, chainbase::database& db) const;
   void removeProxySource(const types::account_name& source, share_type source_stake,
                          chainbase::database& db) const;
   void update_proxied_stake(share_type stake_delta, chainbase::database& db) const;

   /// Cancel proxying votes to @ref proxyTarget for all @ref proxySources
   void cancel_proxies(chainbase::database& db) const;
};

/**
 * @brief The producer_schedule_object class schedules producers into rounds
 *
 * This class stores the state of the virtual race to select runner-up producers, and provides the logic for selecting
 * a round of producers.
 *
 * This is a singleton object within the database; there will only be one stored.
 */
class producer_schedule_object : public chainbase::object<producer_schedule_object_type, producer_schedule_object> {
   OBJECT_CTOR(producer_schedule_object)

   id_type id;
   types::uint128 currentRaceTime = 0;

   /// Retrieve a reference to the producer_schedule_object stored in the provided database
   static const producer_schedule_object& get(const chainbase::database& db) { return db.get(id_type()); }

   /**
    * @brief Calculate the next round of block producers
    * @param db The blockchain database
    * @return The next round of block producers, sorted by owner name
    *
    * This method calculates the next round of block producers according to votes and the virtual race for runner-up
    * producers. Although it is a const method, it will use its non-const db parameter to update its own records, as
    * well as the race records stored in the @ref ProducerVotesObjects
    */
   producer_round calculate_next_round(chainbase::database& db) const;

   /**
    * @brief Reset all producers in the virtual race to the starting line, and reset virtual time to zero
    */
   void reset_producer_race(chainbase::database& db) const;
};

using boost::multi_index::const_mem_fun;
/// Index producers by their owner's name
struct by_owner_name;
/// Index producers by projected race finishing time, from soonest to latest
struct by_projected_race_finish_time;
/// Index producers by votes, from greatest to least
struct by_votes;

using producer_votes_multi_index = chainbase::shared_multi_index_container<
   producer_votes_object,
   indexed_by<
      ordered_unique<tag<by_id>,
         member<producer_votes_object, producer_votes_object::id_type, &producer_votes_object::id>
      >,
      ordered_unique<tag<by_owner_name>,
         member<producer_votes_object, types::account_name, &producer_votes_object::ownerName>
      >,
      ordered_non_unique<tag<by_votes>,
         const_mem_fun<producer_votes_object, std::pair<types::share_type,producer_votes_object::id_type>, &producer_votes_object::get_vote_order>,
         std::greater< std::pair<types::share_type, producer_votes_object::id_type> >
       >,
      ordered_unique<tag<by_projected_race_finish_time>,
         const_mem_fun<producer_votes_object, producer_votes_object::rft_order_type, &producer_votes_object::projected_race_finish_time_order>
      >
/*
   For some reason this does not compile, so I simulate it by adding a new method
      ordered_unique<tag<by_votes>,
         composite_key<
            const_mem_fun<producer_votes_object, types::share_type, &producer_votes_object::get_votes>,
            member<producer_votes_object, producer_votes_object::id_type, &producer_votes_object::id>
         >,
         composite_key_compare< std::greater<types::share_type>, std::less<producer_votes_object::id_type> >
      >*//*,
      */
   >
>;

/// Index proxies by the proxy target account name
struct by_target_name;

using proxy_vote_multi_index = chainbase::shared_multi_index_container<
   proxy_vote_object,
   indexed_by<
      ordered_unique<tag<by_id>, member<proxy_vote_object, proxy_vote_object::id_type, &proxy_vote_object::id>>,
      ordered_unique<tag<by_target_name>, member<proxy_vote_object, types::account_name, &proxy_vote_object::proxy_target>>
   >
>;

using producer_schedule_multi_index = chainbase::shared_multi_index_container<
   producer_schedule_object,
   indexed_by<
      ordered_unique<tag<by_id>,
         member<producer_schedule_object, producer_schedule_object::id_type, &producer_schedule_object::id>
      >
   >
>;

} } // namespace eosio::chain

CHAINBASE_SET_INDEX_TYPE(eosio::chain::producer_votes_object, eosio::chain::producer_votes_multi_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::proxy_vote_object, eosio::chain::proxy_vote_multi_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::producer_schedule_object, eosio::chain::producer_schedule_multi_index)


/*
 * Copyright 2015 David A. Boyuka II
 *
 * 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.
 */

/*
 * test-index-io.cpp
 *
 *  Created on: May 10, 2014
 *      Author: David A. Boyuka II
 */

#include <cstdint>
#include <cassert>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/timer/timer.hpp>

#include "pique/data/grid.hpp"
#include "pique/data/dataset.hpp"
#include "pique/region/region-encoding.hpp"
#include "pique/indexing/binned-index.hpp"
#include "pique/indexing/index-builder.hpp"
#include "pique/setops/setops.hpp"

#include "pique/region/cblq/cblq.hpp"
#include "pique/region/cblq/cblq-encode.hpp"
#include "pique/setops/cblq/cblq-setops.hpp"

#include "standard-datasets.hpp"

using boost::timer::cpu_timer;
using boost::timer::cpu_times;
static double walltime(const cpu_timer &timer) { return (double)timer.elapsed().wall * 1e-9; }

static boost::shared_ptr< InMemoryDataset<int> >
make_dataset(const std::vector<int> &domain) {
	boost::shared_ptr< InMemoryDataset<int> > dataset = boost::make_shared< InMemoryDataset<int> >((std::vector<int>(domain)), Grid{domain.size()});

	return dataset;
}

template<typename RegionEncoderT>
static boost::shared_ptr< BinnedIndex >
make_index(typename RegionEncoderT::RegionEncoderConfig conf, boost::shared_ptr< InMemoryDataset<int> > dataset) {
	typedef typename RegionEncoderT::RegionEncodingOutT RegionEncodingT;
	using SigbitsBinningSpec = SigbitsBinningSpecification< int >;

	IndexBuilder< int, RegionEncoderT, SigbitsBinningSpec > builder(conf, boost::make_shared< SigbitsBinningSpec >(sizeof(int)*8));
	return builder.build_index(*dataset);
}

struct TestCase {
	TestCase(const std::string testname, const std::vector<int> &domain, const std::vector< std::pair<int, int> > &&bin_ranges, NArySetOperation query_combine_op) :
		testname(testname), domain(domain), bin_ranges(bin_ranges), query_combine_op(query_combine_op)
	{}

	const std::string testname;
	const std::vector<int> &domain;
	const std::vector< std::pair<int, int> > bin_ranges;
	const NArySetOperation query_combine_op;
};

template<typename RegionEncoderT, typename SetOperationsT>
static boost::shared_ptr< RegionEncoding > do_test(const TestCase &test, std::string setopsimpl, typename RegionEncoderT::RegionEncoderConfig conf, typename SetOperationsT::SetOperationsConfig setops_conf) {
	typedef typename RegionEncoderT::RegionEncodingOutT RegionEncodingT;

	boost::shared_ptr< SetOperationsT > setops = boost::make_shared< SetOperationsT >(setops_conf);

	boost::shared_ptr< InMemoryDataset<int> > dataset = make_dataset(test.domain);
	boost::shared_ptr< BinnedIndex > index = make_index< RegionEncoderT >(conf, dataset);

	std::vector< boost::shared_ptr< RegionEncoding > > index_regions;
	index->get_regions(index_regions);

	cpu_timer binmerge_timer;
	std::vector< boost::shared_ptr< RegionEncoding > > terms;
	for (auto constr_it = test.bin_ranges.begin(); constr_it != test.bin_ranges.end(); ++constr_it) {
		const std::pair<int, int> bin_range = *constr_it;
		boost::shared_ptr< RegionEncoding > term =
				setops->dynamic_nary_set_op(
						index_regions.begin() + bin_range.first,
						index_regions.begin() + bin_range.second + 1,
						NArySetOperation::UNION);
		terms.push_back(term);
	}
	binmerge_timer.stop();

	cpu_timer termops_timer;
	boost::shared_ptr< RegionEncoding > result =
					setops->dynamic_nary_set_op(
							terms.begin(),
							terms.end(),
							test.query_combine_op);
	termops_timer.stop();

#ifdef VERBOSE_TESTS
	std::cout << "[STATS] Test \"" << test.testname << "\" on setops impl. " << setopsimpl << ": " <<
					"binmerge=" << binmerge_timer.realTime() << ", " <<
					"termops=" << termops_timer.realTime() << std::endl;
#endif

	return result;
}

std::vector<int> && make_big_domain(uint64_t domain_size, int value_range, std::vector<int> &&domain_out) {
	uint64_t i = 0;
	for (; i < (uint64_t)value_range; i++)
		domain_out.push_back(i); // Ensure each key appears at least once
	for (; i < domain_size; i++)
		domain_out.push_back(rand() % value_range); // Fill the rest with randomness

	return static_cast<std::vector<int> &&>(domain_out);
}

// Bin bounds are inclusive, since they represent a [lkey, hkey) range, and even an exclusive hkey implies an inclusive hkey bin read
const std::vector< TestCase > TEST_CASES = {
	TestCase("small-union", SMALL_DOMAIN, std::vector< std::pair<int, int> >{{0, 0}, {2, 2}}, NArySetOperation::UNION),
	TestCase("small-overlap-union", SMALL_DOMAIN, std::vector< std::pair<int, int> >{{0, 1}, {0, 0}}, NArySetOperation::UNION),
	TestCase("small-inter", SMALL_DOMAIN, std::vector< std::pair<int, int> >{{0, 1}, {1, 2}}, NArySetOperation::INTERSECTION),
	TestCase("med-union", MEDIUM_DOMAIN, std::vector< std::pair<int, int> >{{0, 0}, {2, 3}, {5, 5}}, NArySetOperation::UNION),
	TestCase("med-overlap-union", MEDIUM_DOMAIN, std::vector< std::pair<int, int> >{{0, 1}, {1, 5}}, NArySetOperation::UNION),
	TestCase("med-inter", MEDIUM_DOMAIN, std::vector< std::pair<int, int> >{{0, 3}, {3, 5}}, NArySetOperation::INTERSECTION),
	TestCase("big-inter", BIG_DOMAIN, std::vector< std::pair<int, int> >{{2, 6}, {5, 9}}, NArySetOperation::INTERSECTION),
	TestCase("manybin-inter", MANYBINS_DOMAIN, std::vector< std::pair<int, int> >{{60, 120}, {80, 140}}, NArySetOperation::INTERSECTION),
};

// test case small-union:
// [0, 0] = 2202 1110 0001 1101 (in big-endian)
// [2, 2] = 2020 0001 1110 (in big-endian)
// final result = 2222 1111 0001 1110 1101 (big-endian, before compact)

// test case small-inter:
// [0, 1] = 2121 1110 0001 (in big-endian)
// [1, 2] = 2212 0001 1110 0010 (in big-endian)
// final result = 2222 0000 1110 0001 0010 (big-endian, before compact)

int main(int argc, char **argv) {
	char *tempdir_cstr = std::getenv("testworkdir");
	std::string tempdir = (tempdir_cstr ? tempdir_cstr : "test.work.dir");

	std::string indexfile = tempdir + "/tmpdata.query.index";
	std::string datametafile = tempdir + "/tmpdata.meta.index";

	for (auto test_it = TEST_CASES.cbegin(); test_it != TEST_CASES.cend(); test_it++) {
		const TestCase &test = *test_it;
		boost::shared_ptr< RegionEncoding > nondense_oracle = do_test< CBLQRegionEncoder<2>, CBLQSetOperations<2> >(test, "basic", CBLQRegionEncoderConfig(false), CBLQSetOperationsConfig(true));
		boost::shared_ptr< RegionEncoding > nary3 = do_test< CBLQRegionEncoder<2>, CBLQSetOperationsNAry3Dense<2> >(test, "nary3", CBLQRegionEncoderConfig(false), CBLQSetOperationsConfig(true));
		boost::shared_ptr< RegionEncoding > fast = do_test< CBLQRegionEncoder<2>, CBLQSetOperationsFast<2> >(test, "fast", CBLQRegionEncoderConfig(false), CBLQSetOperationsConfig(true));
		boost::shared_ptr< RegionEncoding > naryfast = do_test< CBLQRegionEncoder<2>, CBLQSetOperationsNAry3Fast<2> >(test, "nary3fast", CBLQRegionEncoderConfig(false), CBLQSetOperationsConfig(true));
		assert(*nary3 == *nondense_oracle);
		assert(*fast == *nondense_oracle);
		assert(*naryfast == *nondense_oracle);

		boost::shared_ptr< RegionEncoding > dense_oracle = do_test< CBLQRegionEncoder<2>, CBLQSetOperations<2> >(test, "basic-dense", CBLQRegionEncoderConfig(true), CBLQSetOperationsConfig(true));
		boost::shared_ptr< RegionEncoding > dense_nary3 = do_test< CBLQRegionEncoder<2>, CBLQSetOperationsNAry3Dense<2> >(test, "nary3-dense", CBLQRegionEncoderConfig(true), CBLQSetOperationsConfig(true));
		boost::shared_ptr< RegionEncoding > dense_fast = do_test< CBLQRegionEncoder<2>, CBLQSetOperationsFast<2> >(test, "fast-dense", CBLQRegionEncoderConfig(true), CBLQSetOperationsConfig(true));
		boost::shared_ptr< RegionEncoding > dense_naryfast = do_test< CBLQRegionEncoder<2>, CBLQSetOperationsNAry3Fast<2> >(test, "nary3fast-dense", CBLQRegionEncoderConfig(true), CBLQSetOperationsConfig(true));
		assert(*dense_nary3 == *dense_oracle);
		assert(*dense_fast == *dense_oracle);
		assert(*dense_naryfast == *dense_oracle);
	}
}


#include <boost/program_options.hpp>
#include "physics_test.h"
#include "../src/Inputfile.hpp"
#include "../src/LagrangianState.h"
#include "../src/ParticlePhysics.h"
#include "../src/TimeIntegration.h"
#include <sys/time.h>

using namespace Eigen;

TEST_F(PhysicsTest, testEulerIntegrator) {

  Options options;
  options.inputfile  = std::string(SRCDIR)+"tests/testinput.csv";
  options.outputfile = "final.csv";
  options.dt         = 0.1;
  options.tsteps     = 10;
  options.tsave      = 20;

  // Setup
  LagrangianState simulation(load_csv<MatrixXd>(options.inputfile));
  ParticlePhysics physics(options, simulation);
  TimeIntegration integrator(options, physics, simulation);

  // Solve
  integrator.euler();

  // Output
  simulation.writeXY(options.outputfile);

  // Read the output
  MatrixXd out;
  out   = load_csv<MatrixXd>(options.outputfile);

  ASSERT_TRUE(out.isApprox(Xfinal_));

}

TEST_F(PhysicsTest, testBilliards) {

  Options options;
  options.inputfile  = std::string(SRCDIR)+"tests/billiards.csv";
  options.outputfile = "billiardsfinal_BruteForce";
  options.dt         = 0.001;
  options.tsteps     = 10000;
  options.tsave      = 500;

  Options options_rtree;
  options_rtree.inputfile  = std::string(SRCDIR)+"tests/billiards.csv";
  options_rtree.outputfile = "billiardsfinal_rtree";
  options_rtree.dt         = 0.001;
  options_rtree.tsteps     = 10000;
  options_rtree.tsave      = 500;

  // Setup models
  LagrangianState simulation(load_csv<MatrixXd>(options.inputfile));
  ParticlePhysics physics(options, simulation, false);
  TimeIntegration integrator(options, physics, simulation);

  LagrangianState simulation_rtree(load_csv<MatrixXd>(options_rtree.inputfile));
  ParticlePhysics physics_rtree(options_rtree, simulation_rtree, true);
  TimeIntegration integrator_rtree(options_rtree, physics_rtree, simulation_rtree);

  // Solve
  struct timeval start, end;
  gettimeofday(&start, NULL);
  integrator.euler();
  gettimeofday(&end, NULL);
  double delta = ((end.tv_sec  - start.tv_sec) * 1000000u +
		  end.tv_usec - start.tv_usec) / 1.e6;
  std::cout << "Brute force calculation: " << delta << " s" << std::endl;

  gettimeofday(&start, NULL);
  integrator_rtree.euler();
  gettimeofday(&end, NULL);
  delta = ((end.tv_sec  - start.tv_sec) * 1000000u +
		  end.tv_usec - start.tv_usec) / 1.e6;
  std::cout << "Rtree calculation: " << delta << " s" << std::endl;

  // Output
  simulation.writeXY(options.outputfile);
  simulation_rtree.writeXY(options_rtree.outputfile);

  // Read the output
  MatrixXd out, out2;
  out   = load_csv<MatrixXd>(options.outputfile);
  out2  = load_csv<MatrixXd>(options_rtree.outputfile);

  EXPECT_LT( (out-out2).norm(), 1.e-1);
  EXPECT_LT( (out-XYfinalBilliards_).norm(), 1.e-1);

}

TEST(ContactForceModel, OverlappingStationaryParticlesHaveZeroForce)
{
  Eigen::MatrixXd state(2,5);
  // stationary particle at 0,0 with radius 1
  state(0,0) = 0;
  state(0,1) = 0;
  state(0,2) = 0;
  state(0,3) = 0;
  state(0,4) = 1;
  // stationary particle at 1,0 with radius 1
  state(1,0) = 1;
  state(1,1) = 0;
  state(1,2) = 0;
  state(1,3) = 0;
  state(1,4) = 1;

  BruteForceContactForceModel brute_force_contact;
  RTreeContactForceModel rtree_contact;
  Eigen::MatrixXd forces_brute_force, forces_rtree;

  brute_force_contact.particleContact(LagrangianState(state), forces_brute_force);
  rtree_contact.particleContact(LagrangianState(state), forces_rtree);

  EXPECT_TRUE(forces_brute_force.isApprox(forces_rtree));

  Eigen::MatrixXd zero2x2 = Eigen::MatrixXd::Zero(2,2);
  EXPECT_TRUE(forces_brute_force.isApprox(zero2x2)) << "brute force should be zero.";
  EXPECT_TRUE(forces_rtree.isApprox(zero2x2)) << "rtree force should be zero.";
}

TEST(ContactForceModel, OverlappingInX)
{
  Eigen::MatrixXd state(2,5);
  // particle at 0,0 with radius 0.51 moving at (1,0)
  state(0,0) = 0;
  state(0,1) = 0;
  state(0,2) = 1;
  state(0,3) = 0;
  state(0,4) = 0.51;
  // particle at 1,0 with radius 0.51 moving at (-1,0)
  state(1,0) = 1;
  state(1,1) = 0;
  state(1,2) = -1;
  state(1,3) = 0;
  state(1,4) = 0.51;

  BruteForceContactForceModel brute_force_contact;
  RTreeContactForceModel rtree_contact;
  Eigen::MatrixXd forces_brute_force, forces_rtree;

  brute_force_contact.particleContact(LagrangianState(state), forces_brute_force);
  rtree_contact.particleContact(LagrangianState(state), forces_rtree);

  EXPECT_TRUE(forces_brute_force.isApprox(forces_rtree));

  Eigen::MatrixXd zero2x2 = Eigen::MatrixXd::Zero(2,2);
  EXPECT_FALSE(forces_brute_force.isApprox(zero2x2)) << "brute force should not be zero.";
  EXPECT_FALSE(forces_rtree.isApprox(zero2x2)) << "rtree force should not be zero.";

  Eigen::MatrixXd expected(2,2);
  expected(0,0) = -286.622*3.77;
  expected(1,0) = 286.622*3.77;
  expected(0,1) = 0;
  expected(1,1) = 0;

  EXPECT_TRUE(forces_brute_force.isApprox(expected, 0.1));
  EXPECT_TRUE(forces_rtree.isApprox(expected, 0.1));
}


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/ext/boost/fusion.hpp>

#include <boost/hana/detail/constexpr.hpp>

#include "../helper.hpp"
#include <cassert>
using namespace boost::hana;

BOOST_HANA_CONSTEXPR_LAMBDA auto is_even = [](auto x) {
    return x % 2 == 0;
};

int main() {
    with_nonassociative_forward_sequences([](auto container) {
        assert(replace(is_even, 'x', container()) == container());
        assert(replace(is_even, 'x', container(0)) == container('x'));
        assert(replace(is_even, 'x', container(0, 1)) == container('x', 1));
        assert(replace(is_even, 'x', container(0, 1, 2)) == container('x', 1, 'x'));
        assert(replace(is_even, 'x', container(0, 1, 2, 3)) == container('x', 1, 'x', 3));
    });
}


#include "stdafx.h"
#include "base64_util.h"
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/algorithm/string.hpp>

std::string base64_util::decode(const std::string &val) {
	using namespace boost::archive::iterators;
	using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
	return boost::algorithm::trim_right_copy_if(std::string(It(std::begin(val)), It(std::end(val))), [](char c) {
		return c == '\0';
	});
}

std::string base64_util::encode(const std::string &val) {
	using namespace boost::archive::iterators;
	using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
	auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
	return tmp.append((3 - val.size() % 3) % 3, '=');
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <limits>

#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/transforms.h>

#include <opencv2/opencv.hpp>

#include "parameters.h"
#include "support_plane.h"
#include "utils/cloud_utils.h"
#include "utils/cv_utils.h"
#include "../videoreader/VideoReader.h"

typedef pcl::PointXYZ Point;
typedef pcl::PointCloud<pcl::PointXYZ> Cloud;
typedef pcl::PointCloud<pcl::PointXYZ>::Ptr CloudPtr;

namespace po = boost::program_options;
namespace fs = boost::filesystem;

struct Options
{
  std::string out_file;
  bool debug;
  po::options_description desc;
};

void transform_from_camera_to_lidar(CloudPtr cloud_in_camera_frame, CloudPtr cloud_in_lidar_frame)
{
  Eigen::Matrix4f rot_to_l_from_c = params().rot_to_c_from_l.inverse();
  Eigen::Matrix4f trans_from_c_to_l = params().trans_from_l_to_c.inverse();
  pcl::transformPointCloud(*cloud_in_camera_frame, *cloud_in_lidar_frame, rot_to_l_from_c);
  pcl::transformPointCloud(*cloud_in_lidar_frame, *cloud_in_lidar_frame, trans_from_c_to_l);
}

void transform_from_lidar_to_camera(CloudPtr cloud_in_lidar_frame, CloudPtr cloud_in_camera_frame)
{
  pcl::transformPointCloud(*cloud_in_lidar_frame, *cloud_in_camera_frame, params().trans_from_l_to_c);
  pcl::transformPointCloud(*cloud_in_camera_frame, *cloud_in_camera_frame, params().rot_to_c_from_l);
}

void transform_from_camera_to_lidar(Point& point_in_camera_frame, Point& point_in_lidar_frame)
{
  Eigen::Matrix4f rot_to_l_from_c = params().rot_to_c_from_l.inverse();
  Eigen::Matrix4f trans_from_c_to_l = params().trans_from_l_to_c.inverse();
  Eigen::Transform<float, 3, Eigen::Affine> rot(rot_to_l_from_c);
  Eigen::Transform<float, 3, Eigen::Affine> trans(trans_from_c_to_l);
  point_in_lidar_frame = pcl::transformPoint(point_in_camera_frame, rot);
  point_in_lidar_frame = pcl::transformPoint(point_in_lidar_frame, trans);
}

void transform_from_lidar_to_camera(Point& point_in_lidar_frame, Point& point_in_camera_frame)
{
  Eigen::Transform<float, 3, Eigen::Affine> trans_from_l_to_c(params().trans_from_l_to_c);
  Eigen::Transform<float, 3, Eigen::Affine> rot_to_c_from_l(params().rot_to_c_from_l);
  point_in_camera_frame = pcl::transformPoint(point_in_lidar_frame, trans_from_l_to_c);
  point_in_camera_frame = pcl::transformPoint(point_in_camera_frame, rot_to_c_from_l);
}

int options(int ac, char ** av, Options& opts)
{
  // Declare the supported options.
  po::options_description desc = opts.desc;
  desc.add_options()("help", "Produce help message.");
  desc.add_options()
    ("out", po::value<std::string>(&opts.out_file)->required(), "path to output video file")
    ("debug", po::bool_switch(&opts.debug)->default_value(false), "debug mode")
    ;
  po::variables_map vm;
  po::store(po::parse_command_line(ac, av, desc), vm);

  if (vm.count("help"))
  {
    std::cout << desc << std::endl;
    return 1;
  }

  po::notify(vm);

  return 0;
}

int main(int argc, char** argv)
{
  params().initialize();
  int start = params().start;
  int step = params().step;
  int end = params().end;

  Options opts;
  if (options(argc, argv, opts))
    return 1;

  std::vector<float> filter_range = params().filter_range;
  assert (filter_range.size() == 6);
  float filter_range_min_x = filter_range[0]; float filter_range_max_x = filter_range[1];
  float filter_range_min_y = filter_range[2]; float filter_range_max_y = filter_range[3];
  float filter_range_min_z = filter_range[4]; float filter_range_max_z = filter_range[5];

  std::vector<float> g = params().g;
  Eigen::Vector3f gvec(g[0], g[1], g[2]);
  gvec.normalize();

  // Initialize reader

  VideoReader reader(params().dset_dir, params().dset_avi);
  if (start > 0)
      reader.setFrame(start - 1);

  // Set up a writer as well

  cv::VideoWriter writer;
  int fps = 30;
  //writer.open(argv[1], reader.getCodecType(), fps, cv::Size(reader.getFrameWidth(), reader.getFrameHeight()), true);
  writer.open(opts.out_file, CV_FOURCC( 'M', 'J', 'P', 'G' ), fps, cv::Size(reader.getFrameWidth(), reader.getFrameHeight()), true);
  if (!writer.isOpened())
    std::cout << "Could not open " << opts.out_file << " to write video" << std::endl;

  cv::Mat frame;
  std::vector<cv::Point2f> pixels;
  std::vector<cv::Point2f> filtered_pixels;
  std::vector<cv::Point2f> corner_pixels;

  for (int k = start; k < end; k += step)
  {
    int num_steps = (k - start) / step;

    bool success = reader.getNextFrame(frame);
    if (!success)
    {
        std::cout << "Reached end of video before expected" << std::endl;
        return 1;
    }

    // Load in pcd file

    std::string pcd_file = (fs::path(params().pcd_dir) / fs::path((boost::format("%1%.pcd") % (num_steps)).str())).string();
    CloudPtr cloud (new Cloud);
    std::cout << "loading " << pcd_file << std::endl;
    load_cloud(pcd_file, cloud);

    // Remove NaN's from point cloud

    std::vector<int> indices;
    pcl::removeNaNFromPointCloud(*cloud, *cloud, indices);

    // Filter away everything outside of range
    // Keep this before fitModel

    filter_field(cloud, "x", filter_range_min_x, filter_range_max_x);
    filter_field(cloud, "y", filter_range_min_y, filter_range_max_y);
    filter_field(cloud, "z", filter_range_min_z, filter_range_max_z);

    // Segment plane using RANSAC and filter away points below plane

    SupportPlane<pcl::PointXYZ> support_plane;

    support_plane.setInputCloud(cloud->makeShared());
    int fit_success = support_plane.fitModel(params().inlier_dist_thresh, params().highest_plane);
    if (fit_success == -1)
    {
      PCL_ERROR("Could not find a supporting plane in scene\n");
      return (-1);
    }

    // Points above plane

    CloudPtr cloud_filtered(new Cloud);
    support_plane.filterInliers(cloud_filtered);

    //support_plane.computeHull();
    //support_plane.pointsAboveHull(cloud, cloud_filtered);
    support_plane.pointsAbovePlane(cloud_filtered, cloud_filtered);
    // FIXME
    if (cloud_filtered->size() == 0)
    {
      std::cout << "cloud " << num_steps << " has no points after filtering..." << std::endl;
    }
    else
    {
      // Transform to camera_t
      transform_from_lidar_to_camera(cloud_filtered, cloud_filtered);
      // Only points in front of the camera
      filter_field(cloud_filtered, "z", 0, std::numeric_limits<float>::max());

      // Perform clustering

      std::vector<CloudPtr> clusters;
      extract_clusters_euclidean(cloud_filtered, clusters, params().obj_cluster_tol, params().obj_min_cluster_size, params().obj_max_cluster_size);

      std::cout << "Extracted " << clusters.size() << " clusters" << std::endl;

      // TODO Draw points and bounding boxes on image

      for (int k = 0; k < clusters.size(); k++)
      {
        // Project the point onto the plane (for occluded cars)
        // Have to first transform the cluster back into lidar coordinates

        pcl::PointXYZ min_pt, max_pt;
        transform_from_camera_to_lidar(clusters[k], clusters[k]);
        pcl::getMinMax3D(*(clusters[k]), min_pt, max_pt);
        support_plane.projectPointOnPlane(min_pt, min_pt);
        transform_from_lidar_to_camera(clusters[k], clusters[k]);

        transform_from_lidar_to_camera(min_pt, min_pt);
        transform_from_lidar_to_camera(max_pt, max_pt);

        // Determine 3d bounding box

        CloudPtr corners_cloud(new Cloud);
        get_box_corners(min_pt, max_pt, corners_cloud);
        project_cloud_eigen(corners_cloud, Eigen::Vector3f::Zero(), Eigen::Matrix3f::Identity(),
                params().intrinsics, params().distortions, corner_pixels);
        draw_bbox_3d_from_corner_pixels(corner_pixels, cv::Scalar(0, 0, 255), frame, 2);

        // Project point cloud and draw 2D bounding box

        project_cloud_eigen(clusters[k], Eigen::Vector3f::Zero(), Eigen::Matrix3f::Identity(),
                params().intrinsics, params().distortions, pixels);

        std::vector<int> filtered_pixel_indices;
        filter_pixels(pixels, frame, filtered_pixels, filtered_pixel_indices);
        if (filtered_pixels.size() > params().obj_min_cluster_size)
            set_pixel_colors(filtered_pixels, cv::Vec3b(255, 0, 0), frame, 2);

            // Don't use filtered here
            bbox box = compute_bbox(pixels);
            if (box.xy_ratio() > 0.1) // FIXME PARAM
            {
                draw_bbox(box, cv::Scalar(0, 255, 0), frame, 3);
            }
      }
    }

    if (opts.debug)
    {
      support_plane.visualize(cloud, support_plane.hull_cloud_,
          support_plane.getNormal());
      return 0;
    }

    // Write video

    writer.write(frame);

    // Skip

    success = reader.skip(step - 1);
    if (!success)
    {
      std::cout << "Reached end of video before expected" << std::endl;
      return 1;
    }
  }

  return 0;
}


/*
 * Copyright © 2012, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The NASA Tensegrity Robotics Toolkit (NTRT) v1 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.
*/

/**
 * @file MovingBallSineWaves.cpp
 * @brief Contains the implementation of class MovingBallSineWaves
 * @author Jean Marc Bejjani, Daniele Bissoli
 * @copyright Copyright (C) 2019 LIS
 * $Id$
 */

#include <boost/random.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/uniform_real_distribution.hpp>

// NTRT library
#include "ntrt/core/tgBasicActuator.h"
#include "ntrt/controllers/tgImpedanceController.h"
#include "ntrt/controllers/tgBasicController.h"
#include "ntrt/tgcreator/tgUtil.h"

#include "robotModel.h"
#include "robotController.h"

robotController::robotController(int noiseType, double noiseLevel, int seed)
    : in_controller(new tgImpedanceController(1000, 6130, 0)),
      segments(1.0),
      insideLength(4.5),
      simTime(0.0),
      cycle(0.0),
      target(0.0),
      noiseType(noiseType),
      noiseLevel(noiseLevel),
      seed(seed)
{
    rng.seed(seed);
//    Ks = { 1.0, 1.0, 1.0, 1.0, 1.0 };
// TARGET: 0.4
    Ks = {
        1.5350773654170244,
        1.1682581291061436,
        1.3630111209665405,
        1.3603623969250653,
        1.142116164534608
    }; // best
}
robotController::robotController(int noiseType, double noiseLevel, int seed, std::vector<double>& Ks)
    : in_controller(new tgImpedanceController(1000, 6130, 0)),
      segments(1.0),
      insideLength(4.5),
      simTime(0.0),
      cycle(0.0),
      target(0.0),
      noiseType(noiseType),
      noiseLevel(noiseLevel),
      seed(seed),
      Ks(Ks)
{
    rng.seed(seed);
}

robotController::~robotController()
{
    delete in_controller;
}

void robotController::applyImpedanceControlInside(const std::vector<tgBasicActuator*> stringList, double dt ,
                                                  std::size_t unit, double frequency, double amplitude, double phase)
{

#ifndef SIN_CONTROLLER
    // cycle [0-180]
    cycle = 90 * (sin(simTime * frequency * 2 * M_PI  + phase) + 1.0);

    #ifdef STEP_INPUT
        // this inference is needed only when tests on single module are performed
        // keep the same command for STEP_SIZE (10000 ms that is 10 seconds)
        nn.neuralNet_commands(inputs[int(simTime * 1000) / STEP_SIZE], commands);
        std::cout << "è," << inputs[int(simTime * 1000) / STEP_SIZE];
    #else
        #ifdef DEBUG_INFO
            std::cout << "è," << cycle;
        #endif

        // regular commands (desired cable length) computation
        nn.neuralNet_commands(cycle, commands);
    #endif
#endif

    for(std::size_t i = 0; i < numActuatedCables; i++)
    {
        double noise;
        switch (noiseType)
        {
            case 0: {
                // no noise added (explicitly added for noiseless experiments)
                noise = 0.0;
                break;
            }
            case 1: {
                // gaussian noise added
                boost::random::normal_distribution<> normal_dist(0.0, 1.0);
                noise = normal_dist(rng) * noiseLevel;
                break;
            }
            case 2: {
                // uniform noise added
                boost::random::uniform_real_distribution<> uni_dist(-1.0, 1.0);
                noise = uni_dist(rng) * noiseLevel;
                break;
            }
            default:
                // wrong type values are ignored and no noise is added
                noise = 0.0;
        }
    #ifdef SIN_CONTROLLER
        cycle = sin(simTime * frequency * 2 * M_PI  + phase) + 0.5;
        target = insideLength * (1 - amplitude * (cycle + noise));
    #else
    //    target = AL[i] - (maxCableFraction[i] - commands[i]) * 100 + noise;
        target = AL[i] - (maxCableFraction[i] - commands[i]) * 100 * Ks[i] + noise;
    #endif
    #ifndef STATIC
        double setTension = in_controller->control(*(stringList[i]), dt, target, 0);
    #endif

    #ifdef DEBUG_INFO
        #ifndef COM_COLLECTION
            std::cout << "," << stringList[i]->getCurrentLength();
        #endif
    #endif
    }
}

void robotController::onStep(robotModel& subject, double dt)
{
    simTime += dt;
    segments = subject.m_moduleData.size();

    #ifndef NO_ACTIVE_CABLES
        const std::vector<tgBasicActuator*>& allActuators = subject.getActuators("motor");

        // cycle over all the modules of the robot
        for (std::size_t i = 0; i < segments; i++)
        {
            std::vector<tgBasicActuator*>::const_iterator first = allActuators.begin() + numActuatedCables*i;
            std::vector<tgBasicActuator*>::const_iterator last = allActuators.begin() + numActuatedCables*i+numActuatedCables;
            std::vector<tgBasicActuator*> segmentActuators(first, last);

            applyImpedanceControlInside(segmentActuators, dt, i,
                                        subject.m_moduleData[i].frequency,
                                        subject.m_moduleData[i].amplitude,
                                        subject.m_moduleData[i].phase);
        }
    #else
        // print always 0 as time to notify that no compression is performed
        std::cout << "è,0";
    #endif

    #ifdef COM_COLLECTION
        std::vector<tgBaseRigid*> allRods = subject.find<tgBaseRigid>("rod");

        for (auto& rod : allRods) {
            auto com = rod->centerOfMass();
//            auto com = rod->getPRigidBody()->getCenterOfMassPosition();
            std::cout << "," << com[0] << ";" << com[1] << ";" << com[2];
        }
    #endif
}

void robotController::onTeardown(robotModel& subject) {
    segments = 1.0;
    simTime = cycle = target = 0.0;
    rng.seed(seed);
}


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersSerializeFixture.hpp"
#include "../Deserializer.hpp"

#include <string>

BOOST_AUTO_TEST_SUITE(Deserializer)

struct PadFixture : public ParserFlatbuffersSerializeFixture
{
    explicit PadFixture(const std::string &inputShape,
                        const std::string &padList,
                        const std::string &outputShape,
                        const std::string &dataType)
    {
        m_JsonString = R"(
            {
                inputIds: [0],
                outputIds: [2],
                layers: [
                    {
                        layer_type: "InputLayer",
                        layer: {
                            base: {
                                layerBindingId: 0,
                                base: {
                                    index: 0,
                                    layerName: "InputLayer",
                                    layerType: "Input",
                                    inputSlots: [{
                                        index: 0,
                                        connection: {sourceLayerIndex:0, outputSlotIndex:0 },
                                    }],
                                    outputSlots: [{
                                        index: 0,
                                        tensorInfo: {
                                            dimensions: )" + inputShape + R"(,
                                            dataType: )" + dataType + R"(
                                        }
                                    }]
                                }
                            }
                        }
                    },
                    {
                        layer_type: "PadLayer",
                        layer: {
                            base: {
                                index: 1,
                                layerName: "PadLayer",
                                layerType: "Pad",
                                inputSlots: [{
                                    index: 0,
                                    connection: {sourceLayerIndex:0, outputSlotIndex:0 },
                                }],
                                outputSlots: [{
                                    index: 0,
                                    tensorInfo: {
                                        dimensions: )" + outputShape + R"(,
                                        dataType: )" + dataType + R"(
                                    }
                                }]
                            },
                            descriptor: {
                                padList: )" + padList + R"(,
                            }
                        }
                    },
                    {
                        layer_type: "OutputLayer",
                        layer: {
                            base:{
                                layerBindingId: 2,
                                base: {
                                    index: 2,
                                    layerName: "OutputLayer",
                                    layerType: "Output",
                                    inputSlots: [{
                                        index: 0,
                                        connection: {sourceLayerIndex:1, outputSlotIndex:0 },
                                    }],
                                    outputSlots: [{
                                        index: 0,
                                        tensorInfo: {
                                            dimensions: )" + outputShape + R"(,
                                            dataType: )" + dataType + R"(
                                        },
                                    }],
                                }
                            }
                        },
                    }
                ]
            }
        )";
        SetupSingleInputSingleOutput("InputLayer", "OutputLayer");
    }
};

struct SimplePadFixture : PadFixture
{
    SimplePadFixture() : PadFixture("[ 2, 2, 2 ]",
                                    "[ 0, 1, 2, 1, 2, 2 ]",
                                    "[ 3, 5, 6 ]",
                                    "QuantisedAsymm8") {}
};

BOOST_FIXTURE_TEST_CASE(SimplePadQuantisedAsymm8, SimplePadFixture)
{
    RunTest<3, armnn::DataType::QuantisedAsymm8>(0,
                                                 {
                                                    0, 4, 2, 5, 6, 1, 5, 2
                                                 },
                                                 {
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    4, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
                                                    1, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                                                 });
}

BOOST_AUTO_TEST_SUITE_END()


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <ored/utilities/parsers.hpp>
#include <oret/toplevelfixture.hpp>
#include <ql/time/calendars/all.hpp>
#include <qle/calendars/chile.hpp>
#include <qle/calendars/colombia.hpp>
#include <qle/calendars/malaysia.hpp>
#include <qle/calendars/peru.hpp>
#include <qle/calendars/philippines.hpp>
#include <qle/calendars/thailand.hpp>

using namespace QuantLib;
using namespace QuantExt;
using namespace boost::unit_test_framework;
using namespace std;

namespace bdata = boost::unit_test::data;

using std::ostream;

namespace {

struct TestDatum {
    const char* calendarName;
    Calendar calendar;
};

// Needed for BOOST_DATA_TEST_CASE below as it writes out the TestDatum
ostream& operator<<(ostream& os, const TestDatum& testDatum) {
    return os << "[" << testDatum.calendarName << "," << testDatum.calendar.name() << "]";
}

TestDatum calendarData[] = {{"TGT", TARGET()},
                            {"EUR", TARGET()},
                            {"CHF", Switzerland()},
                            {"USD", UnitedStates()},
                            {"GBP", UnitedKingdom()},
                            {"CAD", Canada()},
                            {"AUD", Australia()},
                            {"JPY", Japan()},
                            {"ZAR", SouthAfrica()},
                            {"SEK", Sweden()},
                            {"ARS", Argentina()},
                            {"BRL", Brazil()},
                            {"CNH", China()},
                            {"CNY", China()},
                            {"CZK", CzechRepublic()},
                            {"DKK", Denmark()},
                            {"FIN", Finland()},
                            {"HKD", HongKong()},
                            {"ISK", Iceland()},
                            {"INR", India()},
                            {"IDR", Indonesia()},
                            {"MXN", Mexico()},
                            {"NZD", NewZealand()},
                            {"NOK", Norway()},
                            {"PLN", Poland()},
                            {"RUB", Russia()},
                            {"SAR", SaudiArabia()},
                            {"SGD", Singapore()},
                            {"KRW", SouthKorea()},
                            {"TWD", Taiwan()},
                            {"TRY", Turkey()},
                            {"UAH", Ukraine()},
                            {"HUF", Hungary()},
                            // Emerging currencies that default to TARGET
                            {"AED", TARGET()},
                            {"BHD", TARGET()},
                            {"CLF", TARGET()},
                            {"CLP", Chile()},
                            {"COP", Colombia()},
                            {"EGP", TARGET()},
                            {"ILS", TARGET()},
                            {"KWD", TARGET()},
                            {"KZT", TARGET()},
                            {"MAD", TARGET()},
                            {"MXV", TARGET()},
                            {"MYR", Malaysia()},
                            {"NGN", TARGET()},
                            {"OMR", TARGET()},
                            {"PEN", Peru()},
                            {"PHP", Philippines()},
                            {"QAR", TARGET()},
                            {"RON", TARGET()},
                            {"THB", QuantExt::Thailand()},
                            {"TND", TARGET()},
                            {"VND", TARGET()},
                            // joint calendars
                            {"US,TARGET", JointCalendar(UnitedStates(), TARGET())},
                            {"NYB,TGT", JointCalendar(UnitedStates(), TARGET())},
                            {"NYB,LNB", JointCalendar(UnitedStates(), UnitedKingdom())},
                            {"LNB,ZUB", JointCalendar(UnitedKingdom(), Switzerland())},
                            {"LNB,NYB,TGT", JointCalendar(UnitedKingdom(), UnitedStates(), TARGET())},
                            {"NYB,ZUB,LNB", JointCalendar(UnitedStates(), Switzerland(), UnitedKingdom())},
                            {"NYB,TRB,LNB", JointCalendar(UnitedStates(), Canada(), UnitedKingdom())},
                            {"TKB,USD,LNB", JointCalendar(Japan(), UnitedStates(), UnitedKingdom())},
                            {"NYB,SYB", JointCalendar(UnitedStates(), Australia())}};

} // namespace

BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)

BOOST_AUTO_TEST_SUITE(CalendarTests)

BOOST_DATA_TEST_CASE(testCalendarNameParsing, bdata::make(calendarData), calendarDatum) {

    Calendar calendar;
    BOOST_REQUIRE_NO_THROW(calendar = ore::data::parseCalendar(calendarDatum.calendarName));
    BOOST_REQUIRE(!calendar.empty());
    BOOST_CHECK_EQUAL(calendar, calendarDatum.calendar);

    BOOST_TEST_MESSAGE("Parsed " << calendarDatum.calendarName << " and got " << calendar.name());
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


#include <boost/hana/difference.hpp>


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2013   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2013   MetaScale SAS
//
//          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
//==============================================================================
#ifndef BOOST_SIMD_MEMORY_DETAILS_ALLOCATOR_WRAPPER_HPP_INCLUDED
#define BOOST_SIMD_MEMORY_DETAILS_ALLOCATOR_WRAPPER_HPP_INCLUDED

#include <boost/config.hpp>

namespace boost { namespace simd { namespace details
{
  template<class T>
  inline T divceil(T a, T b)
  {
    return (a / b) + !!(a % b);
  }

  template<typename Allocator> struct allocator_malloc
  {
    allocator_malloc(Allocator& a) : alloc(a)
    {
    }

    void* operator()(std::size_t sz)
    {
      #ifndef BOOST_NO_EXCEPTIONS
      try
      {
        return alloc.allocate(details::divceil(sz, sizeof(typename Allocator::value_type)));
      }
      catch(...)
      {
        return 0;
      }
      #else
      return alloc.allocate(details::divceil(sz, sizeof(typename Allocator::value_type)));
      #endif
    }

    Allocator& alloc;

    private:
    allocator_malloc& operator=(allocator_malloc const&);
  };

  template<typename Allocator> struct allocator_free
  {
    allocator_free(Allocator& a) : alloc(a) {}

    void operator()(void* ptr, std::size_t sz)
    {
      typedef typename Allocator::pointer type;
      return alloc.deallocate(type(ptr), details::divceil(sz, sizeof(typename Allocator::value_type)));
    }

    Allocator& alloc;

    private:
    allocator_free& operator=(allocator_free const&);
  };
} } }

#endif


#include <iostream>
#include "DebugPlotVisualization.hpp"
#include <QLabel>
#include "qcustomplot.h"
#include <deque>
#include <Eigen/Core>
#include <QAction>
#include <mutex>

using namespace vizkit3d;

struct DebugPlotVisualization::Data {
    std::deque<Eigen::Vector2d> data;
    std::mutex dataMutex;
    QDockWidget* dock;
    QCustomPlot* plot;
    std::string plotName;
    QAction* autoScrollAction;
    QTimer timer;
    const int maxSamples = 20000; //maximum number of samples that is displayed
    const int removeSamples = 2000; //number of samples that is removd if maxSamples is reached
};

DebugPlotVisualization::DebugPlotVisualization()
    : p(new Data)
{
    p->plot = new QCustomPlot();
    p->plot->addGraph();
    p->plot->setContextMenuPolicy(Qt::CustomContextMenu);
    p->plot->setMinimumHeight(200);

    p->dock = new QDockWidget("default name");
    p->dock->setWidget(p->plot);

    p->autoScrollAction = new QAction("auto scroll", p->plot);
    p->autoScrollAction->setCheckable(true);
    p->autoScrollAction->setChecked(true);
    p->autoScrollAction->setToolTip("Use mouse to zoom and drag if auto scroll is disabled");

    connect(p->autoScrollAction, SIGNAL(triggered()), this, SLOT(autoScrollChecked()));
    connect(p->plot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
    connect(&p->timer, SIGNAL(timeout()), this, SLOT(updateUi()));

    p->timer.start(250); //QTimer executes in ui thread
}

void DebugPlotVisualization::autoScrollChecked()
{
    setAutoscroll(p->autoScrollAction->isChecked());
}

void DebugPlotVisualization::setAutoscroll(bool enable)
{
    if(!enable)
    {
        p->plot->setRangeDrag(Qt::Horizontal | Qt::Vertical);
        p->plot->setRangeZoom(Qt::Horizontal | Qt::Vertical);
    }
    else
    {
        p->plot->setRangeDrag(0);
        p->plot->setRangeZoom(0);
    }
}

void DebugPlotVisualization::contextMenuRequest(QPoint pos)
{
  QMenu *menu = new QMenu(p->plot);
//   menu->setAttribute(Qt::WA_DeleteOnClose);
  menu->addAction(p->autoScrollAction);

  menu->popup(p->plot->mapToGlobal(pos));
}

DebugPlotVisualization::~DebugPlotVisualization()
{
    //dtor needs to be defined for unique_ptr pimpl idiom to compile

    //if dock is not managed by qt, delete it manually
    if(p->dock->parent() == nullptr)
        delete p->dock;
}

osg::ref_ptr<osg::Node> DebugPlotVisualization::createMainNode()
{
    return new osg::Group();
}

void DebugPlotVisualization::updateUi()
{
    const bool plotNeedsRedraw = !p->data.empty();

    while(!p->data.empty())
    {
        Eigen::Vector2d dataPoint;
        {
            std::lock_guard<std::mutex> lock(p->dataMutex);

            if(p->data.empty())//some other thread might clear the data while we wait for the lock
                break;

            dataPoint = p->data.front();
            p->data.pop_front();
        }

        p->plot->graph(0)->addData(dataPoint.x(), dataPoint.y());

        if(p->plot->graph(0)->data()->size() > p->maxSamples)
        {
            //removing data is expensive, therefore we remove bigger batches at once
            const double removeKey = (p->plot->graph(0)->data()->begin() + p->removeSamples)->key;
            p->plot->graph(0)->removeDataBefore(removeKey);
        }

        //if auto scroll
        if(p->autoScrollAction->isChecked())
        {
            p->plot->xAxis->setRange(dataPoint.x() - 6, dataPoint.x() + 1);
            p->plot->graph(0)->rescaleValueAxis();
        }
    }

    if(plotNeedsRedraw)
    {
        //invoke to avoid calling repaint from a timer thread
        //For some reason this works while emitting a signal does not, no idea why
        QMetaObject::invokeMethod(p->plot, "replot", Qt::QueuedConnection);
    }

}

void DebugPlotVisualization::updateMainNode(osg::Node* node)
{
    p->dock->setWindowTitle(QString(p->plotName.c_str()));
}

void DebugPlotVisualization::clearData()
{
    //clear all data points that have not been drawn, yet
    {
        std::lock_guard<std::mutex> lock(p->dataMutex);
        p->data.clear();
    }

    p->plot->graph(0)->clearData();
    QMetaObject::invokeMethod(p->plot, "replot", Qt::QueuedConnection);
}

void DebugPlotVisualization::updateDataIntern(vizkit3dDebugDrawings::PlotDataPoint const& value)
{
    {
        std::lock_guard<std::mutex> lock(p->dataMutex);
        p->data.push_back(value.data);
    }

    if(p->plotName != value.name)
    {
        p->plotName = value.name;
        setPluginName(QString("Debug Plot: ") + QString(p->plotName.c_str()));
    }
}

void DebugPlotVisualization::createDockWidgets()
{
    dockWidgets.push_back(p->dock);
}

//Macro that makes this plugin loadable in ruby, this is optional.
VizkitQtPlugin(DebugPlotVisualization)