/*
 * multi_expo.cpp
 *
 *  Created on: 02.07.2012
 *      Author: stephaniebayer
 */

#include "multi_expo.h"
#include "G_q.h"
#include "ElGammal.h";
#include "Pedersen.h"
#include "Cipher_elg.h"
#include <NTL/ZZ.h>
NTL_CLIENT
#include<vector>
using namespace std;
#include <iostream>
#include <time.h>
#include <fstream>

extern G_q G;
extern G_q H;
extern ElGammal El;
extern Pedersen Ped;

multi_expo::multi_expo() {
	// TODO Auto-generated constructor stub

}

multi_expo::~multi_expo() {
	// TODO Auto-generated destructor stub
}

vector<vector<int>*>* multi_expo::to_binary(int win){

	vector<vector<int>* >* ret;
	vector<int>* temp;
	long e,i,j;
	double two= 2;

	e = pow(two, win);
	ret = new vector<vector<int>* >(e);
	for (i = 0; i<e; i++){
		temp = new vector<int>(win);
		for (j=0; j<win; j++){
			temp->at(j) =bit(i,j);
		}
		ret->at(i)=temp;
	}
	return ret;

}

long multi_expo::to_long(vector<int>* bit_r){

	long  t, length;
	double two,i;
	two = 2;

	length =bit_r->size();
	t=0;
	for(i = 0; i<length; i++ ){
		t = t+bit_r->at(i)*pow(two,i);
	}
	return t;
}

void multi_expo::to_long(long& t,vector<int>* bit_r){

	long  length;
	double two,i;
	two = 2;

	length =bit_r->size();
	t=0;
	for(i = 0; i<length; i++ ){
		t = t+bit_r->at(i)*pow(two,i);
	}
}

vector<long>* multi_expo::to_basis(ZZ e, long num_b, int omega){
	long i, j, l, t;
	vector<int>* bit_r;
	vector<long>* basis;
	bit_r = new vector<int>(omega);
	t= num_b/omega +1;
	basis = new vector<long>(t);

	j=0;
	l=0;

	for(i=0; i<num_b; i++){
		bit_r->at(j)=bit(e,i);
		j++;
		if(j==omega){
			to_long(basis->at(l),bit_r);
			j=0;
			l++;
		}
		else if(i == num_b-1){
			for(j = j; j<omega; j++){
				bit_r->at(j)= 0;
				to_long(basis->at(l),bit_r);
			}
		}
	}
	delete bit_r;
	return basis;

}

vector<vector<vector<long>* >* >* multi_expo::to_basis_vec(vector<vector<ZZ>* >* a, long num_b, int omega){
	vector<vector<vector<long>* >* >* basis_vec=0;
	vector<vector<long>* >* basis_row=0;
	vector<long>* basis = 0;
	long i, j, m,n;
	m=a->size();
	n=a->at(0)->size();
	basis_vec = new vector<vector<vector<long>* >* >(m+1);

	for (i = 0; i<m; i++){

		basis_row = new vector<vector<long>* >(n);
		for (j = 0; j<n; j++){
			basis = to_basis(a->at(i)->at(j), num_b, omega);
			basis_row->at(j)= basis;
		}
		basis_vec->at(i+1) = basis_row;
	}
	delete basis;
	return basis_vec;
}

ZZ multi_expo::expo_mult(const vector<ZZ>* e, ZZ ran, int omega_expo, vector<Mod_p>* gen){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	long length;// num_b;
	ZZ prod, p, temp_1, temp_2, mod;
	double two;
	long num_b;
	length = e->size();
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	l = num_b/omega_expo +1;
	basis_vec = new vector<vector<long>* >(length+1);
	basis_vec->at(0) = to_basis(ran, num_b, omega_expo);
	for(i = 0; i<length; i++){
		basis_vec->at(i+1) = to_basis(e->at(i), num_b, omega_expo);
	}
	prod = 1;
	two = 2;
	p=1;
	t = pow(two, omega_expo)-1;
	for(i=l-1; i>0; i--){

		p=1;
		for(j = 0; j<length+1; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p=1;
			for(j = 0; j<length+1; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,gen->at(j).get_val(),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod , prod,temp_2,mod);
		for(k =0; k<omega_expo; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p=1;
	for(j = 0; j<length+1; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,gen->at(j).get_val(),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p=1;
		for(j = 0; j<length+1; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(ZZ& prod, const vector<ZZ>* e, ZZ ran, int omega_expo, vector<Mod_p>* gen){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	long length;// num_b;
	ZZ p, temp_1, temp_2, mod;
	double two;
	long num_b;
	length = e->size();
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	l = num_b/omega_expo +1;
	basis_vec = new vector<vector<long>* >(length+1);
	basis_vec->at(0) = to_basis(ran, num_b, omega_expo);
	for(i = 0; i<length; i++){
		basis_vec->at(i+1) = to_basis(e->at(i), num_b, omega_expo);
	}
	prod = 1;
	two = 2;
	p=1;
	t = pow(two, omega_expo)-1;
	length = length +1;
	for(i=l-1; i>0; i--){

		p=1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p=1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,gen->at(j).get_val(),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod , prod,temp_2,mod);
		for(k =0; k<omega_expo; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p=1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,gen->at(j).get_val(),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p=1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

Cipher_elg multi_expo::expo_mult(const vector<Cipher_elg>* a, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod;
	Cipher_elg prod;
	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i), num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u,temp_1_u,p_u,mod);
			MulMod(temp_2_u,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v, temp_1_v,p_v,mod);
			MulMod(temp_2_v,temp_1_v,temp_2_v,mod);

		}
		MulMod(prod_u,prod_u,temp_2_u,mod);
		MulMod(prod_v,prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u,temp_1_u,p_u,mod);
		MulMod(temp_2_u,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v,temp_1_v,p_v,mod);
		MulMod(temp_2_v,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);

	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(Cipher_elg& prod, const vector<Cipher_elg>* a, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	ZZ ord = H.get_ord();
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod,temp;
	double two;
	long num_b;

	length = a->size();
	mod = H.get_mod();
	num_b = NumBits(ord);
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i), num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v ,temp_1_v,p_v,mod);
			MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);

		}
		prod_u = MulMod(prod_u,temp_2_u,mod);
		prod_v = MulMod(prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

ZZ multi_expo::expo_mult(const vector<ZZ>* a, vector<vector<ZZ>*>* e, int omega, long pos ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ prod, p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j),mod);
				}
			}
			temp_1 = MulMod(temp_1,p,mod);
			temp_2 = MulMod(temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(ZZ& prod, const vector<ZZ>* a, vector<vector<ZZ>*>* e, int omega, long pos ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ  p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j),mod);
				}
			}
			temp_1 = MulMod(temp_1,p,mod);
			temp_2 = MulMod(temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

ZZ multi_expo::expo_mult(const vector<vector<vector<ZZ>* >*>* a, vector<vector<ZZ>*>* e, int omega, long pos, long pos_2 ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ prod, p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;
	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}

	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod,prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(ZZ& prod, const vector<vector<vector<ZZ>* >*>* a, vector<vector<ZZ>*>* e, int omega, long pos, long pos_2 ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ  p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;
	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}

	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod,prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

Cipher_elg multi_expo::expo_mult(const vector<Cipher_elg>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod;
	Cipher_elg prod;
	double two;
	long num_b;

	length = a->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);

	for(i = 0; i<length; i++){
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v,temp_1_v,p_v,mod);
			MulMod(temp_2_v, temp_1_v,temp_2_v,mod);

		}
		MulMod(prod_u,prod_u,temp_2_u,mod);
		MulMod(prod_v,prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u, prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);

	return prod;
}

void multi_expo::expo_mult(Cipher_elg& prod, const vector<Cipher_elg>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod;
	double two;
	long num_b;

	length = a->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);

	for(i = 0; i<length; i++){
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v,temp_1_v,p_v,mod);
			MulMod(temp_2_v, temp_1_v,temp_2_v,mod);

		}
		MulMod(prod_u,prod_u,temp_2_u,mod);
		MulMod(prod_v,prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u, prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);

}

Cipher_elg multi_expo::expo_mult(const vector<Cipher_elg>* a, ZZ f, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	ZZ ord = H.get_ord();
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod,temp;
	Cipher_elg prod;
	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(ord);
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		temp = MulMod(f,e->at(i),ord);
		basis_vec->at(i) = to_basis(temp, num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v ,temp_1_v,p_v,mod);
			MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);

		}
		prod_u = MulMod(prod_u,temp_2_u,mod);
		prod_v = MulMod(prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(Cipher_elg& prod, const vector<Cipher_elg>* a, ZZ f, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	ZZ ord = H.get_ord();
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod,temp;
	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(ord);
	l = num_b/omega +1;
	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		temp = MulMod(f,e->at(i),ord);
		basis_vec->at(i) = to_basis(temp, num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v ,temp_1_v,p_v,mod);
			MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);

		}
		prod_u = MulMod(prod_u,temp_2_u,mod);
		prod_v = MulMod(prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	};
	delete basis_vec;
}

Cipher_elg multi_expo::expo_mult(const vector<vector<Cipher_elg>*>* a, vector<ZZ>* s1, vector<ZZ>* s2, int omega ){
	Cipher_elg prod;
	long i,l;
	l = a->size();
	prod = expo_mult(a->at(0), s1->at(0), s2, omega);
	for(i = 1; i<l; i++){
		prod = prod*expo_mult(a->at(i), s1->at(i), s2, omega);
	}
	return prod;

}

void  multi_expo::expo_mult(Cipher_elg& prod, const vector<vector<Cipher_elg>*>* a, vector<ZZ>* s1, vector<ZZ>* s2, int omega ){
	Cipher_elg temp;
	long i,l;
	l = a->size();
	expo_mult(prod,a->at(0), s1->at(0), s2, omega);
	for(i = 1; i<l; i++){
		expo_mult(temp,a->at(i), s1->at(i), s2, omega);
		Cipher_elg::mult(prod,prod,temp);
	}
}

Mod_p multi_expo::expo_mult(const vector<Mod_p>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;

	long length;
	ZZ prod, p, temp_1, temp_2, mod;
	Mod_p pro;
	double two;
	long num_b;

	length = a->size();
	mod = G.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_temp(length);

	for(i = 0; i<length; i++){
		a_temp.at(i)=a->at(i).get_val();
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a_temp.at(j),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a_temp.at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod, prod,temp_2,mod);
	pro = Mod_p(prod,  mod);
	return pro;
}

void multi_expo::expo_mult(Mod_p& pro, const vector<Mod_p>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;
	long length;
	ZZ prod, p, temp_1, temp_2, mod;
	//Mod_p pro;
	double two;
	long num_b;

	length = a->size();
	mod = G.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_temp(length);

	for(i = 0; i<length; i++){
		a_temp.at(i)=a->at(i).get_val();
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a_temp.at(j),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a_temp.at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod, prod,temp_2,mod);
	pro = Mod_p(prod,  mod);
}

vector<vector<ZZ>* >* multi_expo::calc_Yk(vector<ZZ>* y, int win){
	vector<vector<ZZ>* >* ret;
	vector<ZZ>* temp;
	long h,t, i,j,k,e;
	double two=2;
	ZZ prod, mod, tem;
	mod = H.get_mod();
	vector<vector<int>* >* binary;

	h= y->size()/win;
	e = pow(two, win);
	binary = to_binary(win);

	if((unsigned) h*win ==y->size()){
		ret = new vector<vector<ZZ>* >(h);
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					PowerMod(tem,y->at(i*win+k), binary->at(j)->at(k), mod);
					MulMod(prod,prod,tem,mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
	}
	else{
		ret = new vector<vector<ZZ>* >(h+1);
		t= y->size()-h*win;
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					PowerMod(tem,y->at(i*win+k), binary->at(j)->at(k), mod);
					MulMod(prod,prod,tem,mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
		temp = new vector<ZZ>(e);
		for(j=0; j<e; j++){
			prod = 1;
			for(k=0; k<t; k++){
				PowerMod(tem,y->at(h*win+k), binary->at(j)->at(k), mod);
				MulMod(prod,prod,tem,mod);
			}
			temp->at(j)=prod;
		}
		ret->at(h)=temp;

	}
	for (i = 0; i<e; i++){
		delete binary->at(i);
		binary->at(i)= 0;
	}
	delete binary;
	return ret;

}

vector<vector<Mod_p>* >* multi_expo::calc_Yk(vector<Mod_p>* y, int win){
	vector<vector<Mod_p>* >* ret;
	vector<Mod_p>* temp;
	long h,t, i,j,k,e;
	double two=2;
	Mod_p prod, tem;
	vector<vector<int>* >* binary;

	h= y->size()/win;
	e = pow(two, win);
	binary = to_binary(win);

	if((unsigned)h*win == y->size()){
		ret = new vector<vector<Mod_p>* >(h);
		for(i =0; i<h; i++){
			temp = new vector<Mod_p>(e);
			for(j=0; j<e; j++){
				prod = Mod_p(1,G.get_mod());
				for(k=0; k<win; k++){
					Mod_p::expo(tem,y->at(i*win+k), binary->at(j)->at(k));
					Mod_p::mult(prod,prod,tem);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
	}
	else{
		ret = new vector<vector<Mod_p>* >(h+1);
		t=y->size()-h*win;
		for(i =0; i<h; i++){
			temp = new vector<Mod_p>(e);
			for(j=0; j<e; j++){
				prod = Mod_p(1,G.get_mod());
				for(k=0; k<win; k++){
					Mod_p::expo(tem,y->at(i*win+k), binary->at(j)->at(k));
					Mod_p::mult(prod,prod, tem);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
		temp = new vector<Mod_p>(e);
		for(j=0; j<e; j++){
			prod =  Mod_p(1,G.get_mod());;
			for(k=0; k<t; k++){
				Mod_p::expo(tem,y->at(h*win+k), binary->at(j)->at(k));
				Mod_p::mult(prod,prod,tem);
			}
			temp->at(j)=prod;
		}
		ret->at(h)=temp;

	}
	j = binary->size();
	for(i=0; i<j; i++){
		delete binary->at(i);
		binary->at(i)=0;
	}
	for (i = 0; i<e; i++){
		delete binary->at(i);
		binary->at(i)= 0;
	}
	delete binary;
	return ret;

}
vector<vector<ZZ>* >* multi_expo::calc_Yk(vector<vector<vector<ZZ>*>*>* y, int win, long pos, long pos_2){
	vector<vector<ZZ>* >* ret;
	vector<ZZ>* temp;
	long h,t, i,j,k,e;
	double two=2;
	ZZ prod, mod;
	mod = H.get_mod();
	vector<vector<int>* >* binary;

	h= y->size()/win;
	e = pow(two, win);
	binary = to_binary(win);

	if((unsigned)h*win ==y->size()){
		ret = new vector<vector<ZZ>* >(h);
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					MulMod(prod,prod,PowerMod(y->at(i*win+k)->at(pos_2)->at(pos), binary->at(j)->at(k), mod),mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
	}
	else{
		ret = new vector<vector<ZZ>*>(h+1);
		t= y->size()-h*win;
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					MulMod(prod,prod,PowerMod(y->at(i*win+k)->at(pos_2)->at(pos), binary->at(j)->at(k), mod),mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
		temp = new vector<ZZ>(e);
		for(j=0; j<e; j++){
			prod = 1;
			for(k=0; k<t; k++){
				MulMod(prod,prod,PowerMod(y->at(h*win+k)->at(pos_2)->at(pos), binary->at(j)->at(k), mod),mod);
			}
			temp->at(j)=prod;
		}
		ret->at(h)=temp;

	}
	for (i = 0; i<e; i++){
			delete binary->at(i);
			binary->at(i)= 0;
		}
	delete binary;
	return ret;

}

ZZ multi_expo::multi_expo_LL(vector<ZZ>* y, vector<vector<ZZ>*>* e, int win, long pos){
	vector<vector<ZZ>*>* Yk;
	ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
	return ret;

}

void multi_expo::multi_expo_LL(ZZ& ret, vector<ZZ>* y, vector<vector<ZZ>*>* e, int win, long pos){
	vector<vector<ZZ>*>* Yk;
	//ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;

}

ZZ multi_expo::multi_expo_LL(vector<vector<vector<ZZ>*>*>* y, vector<vector<ZZ>*>* e, int win, long pos, long pos_2){
	vector<vector<ZZ>*>* Yk;
	ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win, pos, pos_2);

	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret, ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
	return ret;

}

void multi_expo::multi_expo_LL(ZZ& ret, vector<vector<vector<ZZ>*>*>* y, vector<vector<ZZ>*>* e, int win, long pos, long pos_2){
	vector<vector<ZZ>*>* Yk;
	//ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win, pos, pos_2);

	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret, ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
}

Mod_p multi_expo::multi_expo_LL(vector<Mod_p>* y, vector<ZZ>* e, int win){
	vector<vector<Mod_p>*>* Yk;
	Mod_p ret;
	double two = 2;
	int expo, i,j,k,h,t,tem;
	t = NumBits(G.get_ord());

	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = Mod_p(1,G.get_mod());
	if((unsigned)win*h==y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
			}
		}
	}
	else{
		tem=y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j),t-1)*pow(two,j-h*win);
		}
		Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret,ret,Yk->at(i)->at(expo));
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo + bit(e->at(j),k)*pow(two,j-h*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));
		}

	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
	return ret;
}

void multi_expo::multi_expo_LL(Mod_p& ret, vector<Mod_p>* y, vector<ZZ>* e, int win){
	vector<vector<Mod_p>*>* Yk;

	double two = 2;
	int expo, i,j,k,h,t,tem;
	long length;

	t = NumBits(G.get_ord());
/*	if(e->size() < y->size()){
		length = e->size();
		h= length/win;
	} else {*/
		length = y->size();
		h = length/win;
	//}
	Yk = calc_Yk(y,win);
	ret = Mod_p(1,G.get_mod());

	if(win*h==length){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
			}
		}
	}
	else{
		tem=length-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j),t-1)*pow(two,j-h*win);
		}
		Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret,ret,Yk->at(i)->at(expo));
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo + bit(e->at(j),k)*pow(two,j-h*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));
		}

	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
}

void multi_expo::multi_expo_LL(ZZ& ret, vector<ZZ>* y, vector<ZZ>* e, int win){
	vector<vector<ZZ>*>* Yk;

	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;

}

void multi_expo::multi_expo_LL(Cipher_elg& ret, Cipher_elg c1, Cipher_elg c2, Cipher_elg c3, Cipher_elg c4, vector<ZZ>* e, int win){

	ZZ ret_u, ret_v;
	vector<ZZ>* c_u;
	vector<ZZ>* c_v;

	c_u = new vector<ZZ>(4);
	c_u->at(0) = c1.get_u();
	c_u->at(1) = c2.get_u();
	c_u ->at(2) = c3.get_u();
	c_u->at(3) = c4.get_u();

	c_v = new vector<ZZ>(4);
	c_v->at(0) = c1.get_v();
	c_v->at(1) = c2.get_v();
	c_v ->at(2) = c3.get_v();
	c_v->at(3) = c4.get_v();

	multi_expo_LL(ret_u, c_u, e, win);
	multi_expo_LL(ret_v, c_v, e, win);
	delete c_u;
	delete c_v;
	ret = Cipher_elg(ret_u, ret_v, H.get_mod());

}

vector<int>* multi_expo::to_basis_sw(ZZ e, long num_b, int omega_sw){

	long i, j, t, te;
	vector<int>* temp;
	vector<int>* basis;
	temp = new vector<int>(omega_sw);
	basis = new vector<int>(num_b);

	i = num_b-1;
	while(i>=omega_sw){
		if (bit(e,i)==0){
			basis->at(i)=0;
			i=i-1;
		}
		else{
			for(t= i-omega_sw+1; t < i+1; t++){
				if(bit(e,t)==1){
					te = i-t;
					for(j = 0; j<= te; j++){
						temp->at(j)=bit(e, t+j);
					}
					for(j =te+1; j<omega_sw; j++){
						temp->at(j) =0;
					}
					break;
				}
			}
			basis->at(t)= to_long(temp);
			for(j = t+1; j<=i; j++){
				basis->at(j)=0;
			}
			i=t-1;
		}
	}
	while(i>=0){
		if (bit(e,i)==0){
			basis->at(i)=0;
			i=i-1;
		}
		else{
			for(t= 0; t < i+1; t++){
				if(bit(e,t)==1){
					te = i-t;
					for(j = 0; j<= te; j++){
						temp->at(j)=bit(e, t+j);
					}
					for(j =te+1; j<omega_sw; j++){
						temp->at(j) =0;
					}
					break;
				}
			}
			basis->at(t)= to_long(temp);
			for(j = t+1; j<=i; j++){
				basis->at(j)=0;
			}
			i=t-1;
		}

	}
	delete temp;
	return basis;
}

ZZ multi_expo::multi_expo_sw( ZZ e_1, ZZ e_2, int omega_sw,  vector<vector<ZZ>* >* gen_prec){
	long i;
	int t_1, t_2;
	ZZ prod, p;
	vector<int>* E_1;
	vector<int>* E_2;
	long num_b;
	ZZ mod;
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	E_1 = to_basis_sw(e_1, num_b, omega_sw);
	E_2 = to_basis_sw(e_2, num_b, omega_sw);
	prod = 1;
	t_1 = 0;
	t_2 = 0;
	t_1 = (E_1->at(num_b-1)+1)/2;
	t_2 = (E_2->at(num_b-1)+1)/2;
	if ( t_1> 0){
		MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
	}
	if ( t_2> 0){
		MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
	}
	for(i = num_b-2; i>=0; i--){
		t_1 = (E_1->at(i)+1)/2;
		t_2 = (E_2->at(i)+1)/2;
		SqrMod(prod,prod, mod);
		if ( t_1> 0){
			MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
		}
		if ( t_2> 0){
			MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
		}
	}
	delete E_1;
	delete E_2;
	 return prod;
}

void multi_expo::multi_expo_sw(ZZ& prod, ZZ e_1, ZZ e_2, int omega_sw, vector<vector<ZZ>* >* gen_prec){
	long i;
	int t_1, t_2;
	ZZ p;
	vector<int>* E_1;
	vector<int>* E_2;
	long num_b;
	ZZ mod;
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	E_1 = to_basis_sw(e_1, num_b, omega_sw);
	E_2 = to_basis_sw(e_2, num_b, omega_sw);
	prod = 1;
	t_1 = 0;
	t_2 = 0;
	t_1 = (E_1->at(num_b-1)+1)/2;
	t_2 = (E_2->at(num_b-1)+1)/2;
	if ( t_1> 0){
		MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
	}
	if ( t_2> 0){
		MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
	}
	for(i = num_b-2; i>=0; i--){
		t_1 = (E_1->at(i)+1)/2;
		t_2 = (E_2->at(i)+1)/2;
		SqrMod(prod,prod, mod);
		if ( t_1> 0){
			MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
		}
		if ( t_2> 0){
			MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
		}
	}
	delete E_1;
	delete E_2;
	// return prod;
}


#include <fstream>
#include <iostream>
#include <vector>

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

#include "lm/model.hh"
#include "lm/enumerate_vocab.hh"

namespace po = boost::program_options;
using namespace std;

lm::ngram::ProbingModel* ngram;
struct GetVocab : public lm::ngram::EnumerateVocab {
  GetVocab(vector<lm::WordIndex>* out) : out_(out) { }
  void Add(lm::WordIndex index, const StringPiece &str) {
    out_->push_back(index);
  }
  vector<lm::WordIndex>* out_;
};

bool InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("model,m",po::value<string>(),"n-gram language model file (KLM)");
  po::options_description clo("Command line options");
  clo.add_options()
        ("config", po::value<string>(), "Configuration file")
        ("help,h", "Print this help message and exit");
  po::options_description dconfig_options, dcmdline_options;
  po::positional_options_description p;
  p.add("grammar", -1);

  dconfig_options.add(opts);
  dcmdline_options.add(opts).add(clo);

  po::store(po::command_line_parser(argc, argv).options(dcmdline_options).positional(p).run(), *conf);
  if (conf->count("config")) {
    ifstream config((*conf)["config"].as<string>().c_str());
    po::store(po::parse_config_file(config, dconfig_options), *conf);
  }
  po::notify(*conf);

  if (conf->count("help")) {
    cerr << "Usage " << argv[0] << " [OPTIONS]\n";
    cerr << dcmdline_options << endl;
    return false;
  }
  return true;
}

template <class Model> double BlanketProb(const vector<lm::WordIndex>& sentence, const lm::WordIndex word, const int subst_pos, const Model &model) {
  typename Model::State state, out;
  lm::FullScoreReturn ret;
  double total = 0;
  state = model.NullContextState();

  const int begin = max(subst_pos - model.Order() + 1, 0);
  const int end = min(subst_pos + model.Order(), (int)sentence.size());
  int lookups = 0;
  bool have_full_context = false;
  for (int i = begin; i < end; ++i) {
    if (i == 0) {
      state = model.BeginSentenceState();
      have_full_context = true;
    } else {
      lookups++;
      if (lookups == model.Order()) { have_full_context = true; }
      ret = model.FullScore(state, (subst_pos == i ? word : sentence[i]), out);
      if (have_full_context) { total += ret.prob; }
      state = out;
    }
  }
  return total;
}

int main(int argc, char** argv) {
  po::variables_map conf;
  if (!InitCommandLine(argc, argv, &conf)) return 1;
  lm::ngram::Config kconf;
  vector<lm::WordIndex> vocab;
  GetVocab gv(&vocab);
  kconf.enumerate_vocab = &gv;
  ngram = new lm::ngram::ProbingModel(conf["model"].as<string>().c_str(), kconf);
  cerr << "Loaded " << (int)ngram->Order() << "-gram KenLM (vocab size=" << vocab.size() << ")\n";
  vector<int> exclude(vocab.size(), 0);
  exclude[0] = 1; // exclude OOVs

  double prob_sum = 0;
  int counter = 0;
  int rank_error = 0;
  string line;
  while (getline(cin, line)) {
    stringstream line_stream(line);
    vector<string> tokens;
    tokens.push_back("<s>");
    string token;
    while (line_stream >> token)
      tokens.push_back(token);
    tokens.push_back("</s>");

    vector<lm::WordIndex> sentence(tokens.size());
    for (int i = 0; i < tokens.size(); ++i)
      sentence[i] = ngram->GetVocabulary().Index(tokens[i]);
    exclude[sentence[0]] = 1;
    exclude[sentence.back()] = 1;
    for (int i = 1; i < tokens.size()-1; ++i) {
      cerr << tokens[i] << endl;
      ++counter;
      lm::WordIndex gold = sentence[i];
      double blanket_prob = BlanketProb<lm::ngram::ProbingModel>(sentence, gold, i, *ngram);
      double z = 0;
      for (int v = 0; v < vocab.size(); ++v) {
        if (exclude[v]) continue;
        double lp = BlanketProb<lm::ngram::ProbingModel>(sentence, v, i, *ngram);
        if (lp > blanket_prob) ++rank_error;
        z += pow(10.0, lp);
      }
      double post_prob = blanket_prob - log10(z);
      cerr << "  " << post_prob << endl;
      prob_sum -= post_prob;
    }
  }
  cerr << "perplexity=" << pow(10,prob_sum/(double)counter) << endl;
  cerr << "Rank error=" << rank_error/(double)counter << endl;

  return 0;
}


//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020 Nikita Kaskov <nbering@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_BLAKE2B_FUNCTIONS_HPP
#define CRYPTO3_BLAKE2B_FUNCTIONS_HPP

#include <array>

#include <boost/crypto3/hash/detail/blake2b/blake2b_policy.hpp>

#include <boost/static_assert.hpp>

namespace boost {
    namespace crypto3 {
        namespace hashes {
            namespace detail {
                template<std::size_t DigestBits>
                struct blake2b_functions : public blake2b_policy<DigestBits> {
                    typedef blake2b_policy<DigestBits> policy_type;

                    typedef typename policy_type::word_type word_type;

                    constexpr static const std::size_t state_words = policy_type::state_words;

                    inline static void g(word_type &a, word_type &b, word_type &c, word_type &d, word_type M0,
                                         word_type M1) {
                        a = a + b + M0;
                        d = policy_type::template rotr<32>(d ^ a);
                        c = c + d;
                        b = policy_type::template rotr<24>(b ^ c);
                        a = a + b + M1;
                        d = policy_type::template rotr<16>(d ^ a);
                        c = c + d;
                        b = policy_type::template rotr<63>(b ^ c);
                    }

                    template<size_t i0, size_t i1, size_t i2, size_t i3, size_t i4, size_t i5, size_t i6, size_t i7,
                             size_t i8, size_t i9, size_t iA, size_t iB, size_t iC, size_t iD, size_t iE, size_t iF>
                    inline static void round(std::array<word_type, state_words * 2> &v,
                                             const std::array<word_type, state_words * 2> &M) {
                        g(v[0], v[4], v[8], v[12], M[i0], M[i1]);
                        g(v[1], v[5], v[9], v[13], M[i2], M[i3]);
                        g(v[2], v[6], v[10], v[14], M[i4], M[i5]);
                        g(v[3], v[7], v[11], v[15], M[i6], M[i7]);
                        g(v[0], v[5], v[10], v[15], M[i8], M[i9]);
                        g(v[1], v[6], v[11], v[12], M[iA], M[iB]);
                        g(v[2], v[7], v[8], v[13], M[iC], M[iD]);
                        g(v[3], v[4], v[9], v[14], M[iE], M[iF]);
                    }
                };
            }    // namespace detail
        }        // namespace hashes
    }            // namespace crypto3
}    // namespace boost

#endif    // CRYPTO3_BLAKE2B_FUNCTIONS_HPP


#include "qlearner/qlearner.hpp"
#include "qlearner/stats.h"
#include "qlearner/action.h"
#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>
#include <boost/lexical_cast.hpp>

#include "config.h"
#include "net/fc_layer.hpp"
#include "net/relu_layer.hpp"
#include "net/tanh_layer.hpp"
#include "net/solver.hpp"
#include "net/rmsprop.hpp"
#include "net/network.hpp"

#include <random>

using namespace net;
using namespace qlearn;

const float BAT_SIZE = 0.05;

Vector dconv(float val, float min, float max, int steps, Vector result)
{
	float p = (val - min) / (max - min);
	int rp = std::min(steps-1, std::max(0, int(steps * p)));
	result = Matrix::Zero(steps, 1);
	result[rp] = 1;
	return std::move(result);
}

struct PongGame
{
	float ballx;
	float bally;
	float bvx;
	float bvy;
	float posy;

	PongGame()
	{

	}

	void reset()
	{
		ballx = 0.6;
		bally = (rand() % 101) / 100.f;
		bvx = 1;
		bvy = (rand() % 101 - 50) / 20.f;
		posy = (rand() % 101) / 100.f;
	}

	float step( int ac )
	{
		ballx += 0.01*bvx;
		bally += 0.01*bvy;

		if( ac == 1 )
			posy += 0.025;
		else if(ac == 2)
			posy -= 0.025;

		if(bally > 1)
		{
			bally = 2 - bally;
			bvy *= -1;
		}

		if(bally < 0)
		{
			bally = -bally;
			bvy *= -1;
		}

		if( ballx > 1.0 )
		{
			if( std::abs(bally - posy) < BAT_SIZE )
				return 1;
			else
				return -1;
		}

		return 0;
	}

	const Vector& data() const
	{
		vec.resize (5);
		vec[0] = ballx;
		vec[1] = bally;
		vec[2] = posy;
		vec[3] = bvx;
		vec[4] = bvy;
		/*vec.resize(30);
		auto d1 = dconv(bally, 0, 1, 15, std::move(dccache1));
		auto d2 = dconv(posy, 0, 1, 15, std::move(dccache2));
		vec << d1, d2;
		dccache1 = std::move(d1);
		dccache2 = std::move(d2);*/
		return vec;
	}

private:
	mutable Vector vec;
	mutable Vector dccache1;
	mutable Vector dccache2;
};

void build_image(const qlearn::QLearner& l);

std::mutex mTargetNet;
std::atomic<bool> evaluate(false);
std::atomic<bool> run(true);

void learn_thread( Network& target_net, ComputationGraph& graph )
{
	Config config(5, 3, 2000000);
	config.epsilon_steps(2000000).update_interval(10000).batch_size(32).init_memory_size(10000).init_epsilon_time(100000)
		.discount_factor(0.98);

	Network network;
	network << FcLayer(Matrix::Random(10, 5).array() / 5);
	network << TanhLayer(Matrix::Zero(10, 1));
	network << FcLayer(Matrix::Random(10, 10).array() / 5);
	network << TanhLayer(Matrix::Zero(10, 1));
	network << FcLayer(Matrix::Random(3, 10).array() / 5);
	network << TanhLayer(Matrix::Zero(3, 1));

	qlearn::QLearner learner( config, std::move(network) );

	auto prop = std::unique_ptr<RMSProp>(new RMSProp(0.9, 0.0005, 0.001));
	RMSProp* rmsprop = prop.get();
	Solver solver( std::move(prop) );

	std::fstream rewf("reward.txt", std::fstream::out);

	PongGame game;
	game.reset();
	int ac = 0;
	int games = 0;
	auto last_time = std::chrono::high_resolution_clock::now();
//	bool run = true;

	learner.setCallback( [&](const QLearner& learner, const Stats& stats )
	{
		std::cout << games << ": " << learner.getCurrentEpsilon() << "\n";
		std::cout << stats.getSmoothReward() << " (" <<  stats.getSmoothQVal() << ", " << stats.getSmoothMSE() << ")\n";
		rewf << stats.getSmoothReward() << "\t" <<  stats.getSmoothQVal() << "\t" << stats.getSmoothMSE() << " " << learner.getCurrentEpsilon()  << "\n";
		rewf.flush();
//		std::cout << learner.getNumberLearningSteps() << "\n";
		build_image(learner);
		std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(
					std::chrono::high_resolution_clock::now() - last_time).count() << " ms\n";
		last_time = std::chrono::high_resolution_clock::now();
//		std::cout << Eigen::internal::malloc_counter() << "\n";
		std::cout << " - - - - - - - - - - \n";
		std::lock_guard<std::mutex> lck(mTargetNet);
		target_net = learner.network().clone();
		graph = ComputationGraph(target_net);
		evaluate = true;
	} );

	while(run)
	{
		/*if(games < 1000)
		{
			rmsprop->setRate(0.001);
		} else if ( games < 5000 )
		{
			rmsprop->setRate(0.005);
		} else
		{
			rmsprop->setRate(0.0001);
		}*/

		float r = game.step(ac);
		ac = learner.learn_step( game.data(), r, game.ballx >= 1, solver );
		if( game.ballx >= 1.0 )
		{
			game.reset();
			games++;
		}
	}
}

extern int status;
/*int main()
{
	Network network;
	ComputationGraph graph(network);
	std::thread learner( learn_thread, std::ref(network), std::ref(graph));
	learner.detach();

	PongGame game;
	game.reset();
	int games = 0;

	std::fstream evl("test.txt", std::fstream::out);

	int step = 0;
	while(true)
	{
		//step++;
		//float v;
		//{
		//	std::lock_guard<std::mutex> lck(mTargetNet);
		//	auto ac = getAction(graph, game.data());
		//	game.step(ac.id);
		//	v = ac.score;
		//}

		//if( game.ballx > 1.0 )
		//{
		//	game.reset();
		//	games++;
		//	sleep(100);
		//}

		if(evaluate)
		{
			Network copy = network.clone();
			ComputationGraph graph(copy);
			float reward = 0;
			for(int g = 0; g < 200; ++g)
			{
				PongGame game;
				game.reset();
				for(int s = 0; s < 100; ++s)
				{
					auto ac = getAction(graph, game.data());
					reward += game.step(ac.id);
					if( game.ballx > 1.0 ) break;
				}
			}
			std::cout << reward << "\n";
			evl << reward << "\n";
			evl.flush();
			evaluate = false;
			sleep (10);
		}
	}
	run = false;
} */

void build_image(const QLearner& l)
{

}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2013   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2011 - 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_ARITHMETIC_FUNCTIONS_FMS_HPP_INCLUDED
#define BOOST_SIMD_ARITHMETIC_FUNCTIONS_FMS_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

namespace boost { namespace simd { namespace tag
  {
    /*!
      @brief  fms generic tag

      Represents the fms function in generic contexts.

      @par Models:
      Hierarchy
    **/
    struct fms_ : ext::elementwise_<fms_>
    {
      /// @brief Parent hierarchy
      typedef ext::elementwise_<fms_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_fms_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
    template<class Site, class... Ts>
    BOOST_FORCEINLINE generic_dispatcher<tag::fms_, Site> dispatching_fms_(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
    {
      return generic_dispatcher<tag::fms_, Site>();
    }
    template<class... Args>
    struct impl_fms_;
  }

  /*!
    Computes the fused multiply-substract of three value.

    @par semantic:
    For any given value @c x,  @c y,  @c z of type @c T:

    @code
    T r = fms(x, y, z);
    @endcode

    The code is similar to:

    @code
    T r = x*y-z;
    @endcode

    @param  a0
    @param  a1
    @param  a2

    @return a value of the same type as the input.
  **/
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::fms_, fms, 3)

  /*!
    Computes the fused negated substract-multiply of three values.

    @par semantic:
    For any given value @c x,  @c y,  @c z of type @c T:

    @code
    T r = fnsm(x, y, z);
    @endcode

    The code is similar to:

    @code
    T r = -(x-y*z);
    @endcode

    @param  a0
    @param  a1
    @param  a2

    @return a value of the same type as the input.
  **/
  BOOST_DISPATCH_FUNCTION_INTERFACE(tag::fms_, fnsm, 3)
  {
    typename boost::dispatch::make_functor<tag::fms_, A0>::type callee;
    return callee(a2, a1, a0);
  }
} }

#endif


// Author(s): Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
/// \file remove_equations_test.cpp
/// \brief Add your file description here.

#define BOOST_TEST_MODULE remove_equations_test
#include <boost/test/included/unit_test.hpp>
#include "mcrl2/pbes/detail/pbes_property_map.h"
#include "mcrl2/pbes/remove_equations.h"
#include "mcrl2/pbes/txt2pbes.h"

using namespace mcrl2;
using namespace mcrl2::pbes_system;

void test_remove_unreachable_variables(const std::string& pbes_spec, const std::string& expected_result)
{
  pbes p = txt2pbes(pbes_spec);
  pbes q = p;
  remove_unreachable_variables(q);
  BOOST_CHECK(q.is_well_typed());
  pbes_system::detail::pbes_property_map info1(q);
  pbes_system::detail::pbes_property_map info2(expected_result);
  std::string diff = info1.compare(info2);
  if (!diff.empty())
  {
    std::cerr << "\n------ FAILED TEST ------" << std::endl;
    std::cerr << "--- expected result" << std::endl;
    std::cerr << expected_result << std::endl;
    std::cerr << "--- found result" << std::endl;
    std::cerr << info1.to_string() << std::endl;
    std::cerr << "--- differences" << std::endl;
    std::cerr << diff << std::endl;
  }
  BOOST_CHECK(diff.empty());
}

BOOST_AUTO_TEST_CASE(test_remove_unreachable_variables1)
{
  std::string pbesspec =
    "pbes nu X1 = X2 && X3;                                   \n"
    "     nu X2 = X4 && X1;                                   \n"
    "     nu X3 = true;                                       \n"
    "     nu X4 = false;                                      \n"
    "     nu X5 = X6;                                         \n"
    "     nu X6 = X5;                                         \n"
    "                                                         \n"
    "init X1;                                                 \n"
    ;
  std::string bnd = "binding_variable_names = X1, X2, X3, X4";
  test_remove_unreachable_variables(pbesspec, bnd);
}

BOOST_AUTO_TEST_CASE(test_remove_unreachable_variables2) {
    std::string pbesspec =
            "pbes                      \n"
            " nu X(n:Nat) = Y && X(n); \n"
            " mu Y = Z;                \n"
            " nu Z = Y;                \n"
            " nu U = U;                \n"
            "                          \n"
            " init X(0);               \n"
    ;
    std::string bnd = "binding_variable_names = X, Y, Z";
    test_remove_unreachable_variables(pbesspec, bnd);
}


#ifndef YANNQ_OBSERVABLES_ENERGY_HPP
#define YANNQ_OBSERVABLES_ENERGY_HPP
#include <Eigen/Dense>
#include "Observables/Observable.hpp"
#include "Utilities/Utility.hpp"

namespace yannq
{
template<typename T, class Hamiltonian>
class Energy
	: public Observable<Energy<T, Hamiltonian> >
{
public:
	using Scalar = T;
	using RealScalar = typename remove_complex<Scalar>::type;

	using Matrix = typename Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
	using Vector = typename Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
	using RealMatrix = typename Eigen::Matrix<RealScalar, Eigen::Dynamic, Eigen::Dynamic>;
	using RealVector = typename Eigen::Matrix<RealScalar, Eigen::Dynamic, 1>;

private:
	const Hamiltonian& ham_;

	Vector elocs_;
	Scalar eloc_;
	Scalar elocVar_;

public:
	Energy(const Hamiltonian& ham)
		: ham_(ham)
	{
	}

	template<class State>
	double operator()(State&& st) const
	{
		return ham_(st);
	}

	void initIter(uint32_t nsmp)
	{
		elocs_.setZero(nsmp);
		elocVar_ = 0.;
	}

	template<class State>
	inline void eachSample(uint32_t n, State&& state)
	{
		elocs_(n) = ham_(state);
	}

	void finIter()
	{
		eloc_ = real(elocs_.mean());
		elocVar_ = elocs_.real().cwiseAbs2().mean();
		elocVar_ -= eloc_*eloc_;
	}

	template<typename MatrixT>
	void finIter(const Eigen::MatrixBase<MatrixT>& weights)
	{
		assert(weights.cols() == 1);
		eloc_ = weights.transpose()*elocs_.real();
		elocVar_ = weights.transpose()*elocs_.real().cwiseAbs2();
		elocVar_ -= eloc_*eloc_;
	}

	const Vector& elocs() const &
	{
		return elocs_;
	}

	Vector&& elocs() &&
	{
		return std::move(elocs_);
	}

	Scalar eloc() const
	{
		return eloc_;
	}

	Scalar elocVar() const
	{
		return elocVar_;
	}
};

}

#endif//YANNQ_OBSERVABLES_ENERGY_HPP


// Copyright (c) 2015
// Author: Chrono Law
#include <std.hpp>
using namespace std;

//#define BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::posix_time;

//////////////////////////////////////////

void case1()
{
    date d(2014,11,3);
    date_facet* dfacet = new date_facet("%Y年%m月%d日");
    cout.imbue(locale(cout.getloc(), dfacet));
    cout << d << endl;

    time_facet *tfacet = new time_facet("%Y年%m月%d日%H点%M分%S%F秒");
    cout.imbue(locale(cout.getloc(), tfacet));
    cout << ptime(d , hours(21) + minutes(50) + millisec(100)) << endl;

}

//////////////////////////////////////////
days operator"" _D(unsigned long long n)
{
    return days(n);
}

weeks operator"" _W(unsigned long long n)
{
    return weeks(n);
}

date operator"" _YMD(const char*  s, std::size_t )
{
    return from_string(s);
}

void case2()
{
    auto d = 100_D;
    auto w = 1_W;

    assert(d.days() == 100);
    assert(w.days() == (7_D).days());

    auto today = "2014/11/3"_YMD;
    cout  << today << endl;
}

//////////////////////////////////////////
#include <boost/date_time/local_time/local_time.hpp>
using namespace boost::local_time;

void case3()
{
    tz_database tz_db;
    tz_db.load_from_file("./date_time_zonespec.csv");

    time_zone_ptr shz =  tz_db.time_zone_from_region("Asia/Shanghai");

    time_zone_ptr sfz =  tz_db.time_zone_from_region("America/Los_Angeles");

    cout << shz->has_dst() << endl;
    cout << shz->std_zone_name() << endl;

    local_date_time dt_bj(date(2014,3,6),
            hours(16),
            shz,
            false);
    cout << dt_bj << endl;

    time_duration flight_time = hours(12);
    dt_bj += flight_time;
    cout << dt_bj << endl;
    local_date_time dt_sf = dt_bj.local_time_in(sfz);
    cout << dt_sf;
}

//////////////////////////////////////////

int main()
{
    case1();
    case2();
    case3();
}


/**********************************************************************
example.cpp - example of interfacing of OpenBabel with Boost Graph Library

Copyright (C) 2007 by Gerde Menche

This file is part of the Open Babel project.
For more information, see <http://openbabel.org/>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

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

#include "babelconfig.h"
#include "openbabel/mol.h"
#include "openbabel/obconversion.h"

#include <iostream>
//#include <unistd.h>

#include "mol_graph.h"
#include "verbose_visitors.h"

#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/undirected_dfs.hpp>
#include <boost/pending/queue.hpp>

using namespace std;
using OpenBabel::OBMol;
using OpenBabel::OBAtom;
using OpenBabel::OBBond;
using OpenBabel::OBConversion;
using OpenBabel::OBFormat;

int main(int argc,char **argv)
{
    int c;
    char *FileIn = NULL;

    if (argc != 2)
    {
        cout << "give file name\n";
        return 1;
    }

    FileIn  = argv[1];

    // Find Input filetype
    OBConversion conv;
    OBFormat *format = conv.FormatFromExt(FileIn);

    if (!format || !conv.SetInAndOutFormats(format, format))
    {
        cerr << argv[0] << ": cannot read input format!" << endl;
        exit (-1);
    }

    ifstream ifs;

    // Read the file
    ifs.open(FileIn);
    if (!ifs)
    {
        cerr << argv[0] << ": cannot read input file!" << endl;
        exit (-1);
    }

    OBMol mol;

    for (c=0;;)
    {
        mol.Clear();
	conv.Read(&mol, &ifs);
        if (mol.Empty())
            break;
        OBAtom* atom = *(mol.BeginAtoms());

        // Examples using breadth first search

        std::cout << "\nBREADTH FIRST SEARCH\n";

        boost::queue< OBAtom* > buffer;
        boost::breadth_first_search( mol,atom,buffer, OpenBabel::OBVerboseBFSVisitor(cout),
            OBAtomMap< boost::default_color_type >(mol) );

        OBAtomMap< OBAtom* >predecessor_map_bfs(mol,(OBAtom*)0);
        OBAtomMap< int >distance_map_bfs(mol, 0);

        boost::breadth_first_search( mol,atom,buffer,
            boost::make_bfs_visitor( std::make_pair (
                boost::record_predecessors(predecessor_map_bfs,boost::on_tree_edge()),
                boost::record_distances(distance_map_bfs,boost::on_tree_edge()) )),
            OBAtomMap< boost::default_color_type >(mol) );

        for ( OpenBabel::OBAtomIterator beg = mol.BeginAtoms();
             beg != mol.EndAtoms(); ++beg )
        {
            OBAtom* parent = predecessor_map_bfs[*beg];
            if ( parent )
            {
                std::cout << (*beg)->GetIdx() - 1 << " has parent : "
                          << parent->GetIdx() - 1 << "\t";
            }
            else
            {
                std::cout << (*beg)->GetIdx() - 1 << " has no parent\t\t";
            }
            std::cout << " graph theoretical distance : " << distance_map_bfs[*beg] << "\n";
        }

        std::vector< boost::graph_traits<OpenBabel::OBMol>::edge_descriptor > bv;
        boost::breadth_first_search( mol,atom,buffer,
            boost::make_bfs_visitor(
                boost::record( std::back_inserter(bv), boost::on_gray_target() )),
            OBAtomMap< boost::default_color_type >(mol) );

        std::cout << "\nBACK EDGES\n";
        for ( std::vector< boost::graph_traits<OpenBabel::OBMol>::edge_descriptor >::iterator
              beg = bv.begin(), end = bv.end();
              beg != end; ++beg )
        {
            std::cout << '(' << (*beg)->GetBeginAtom()->GetIdx() << ")--";
            std::cout << (*beg)->GetIdx() << "--(";
            std::cout << (*beg)->GetEndAtom()->GetIdx() << ")\n";
        }

        // Examples using depth first search

        std::cout << "\nDEPTH FIRST SEARCH\n";

        boost::undirected_dfs( mol, OpenBabel::OBVerboseDFSVisitor(cout),
            OBAtomMap< boost::default_color_type >(mol),
            OBBondMap< boost::default_color_type >(mol),
            atom );

        OBAtomMap< OBAtom* >predecessor_map_dfs(mol,(OBAtom*)0);
        OBAtomMap< int >distance_map_dfs(mol,0);

        boost::undirected_dfs( mol,
            boost::make_dfs_visitor( std::make_pair (
                boost::record_predecessors(predecessor_map_dfs,boost::on_tree_edge()),
                boost::record_distances(distance_map_dfs,boost::on_tree_edge()) )),
            OBAtomMap< boost::default_color_type >(mol),
            OBBondMap< boost::default_color_type >(mol),
            atom );

        for ( OpenBabel::OBAtomIterator beg = mol.BeginAtoms();
              beg != mol.EndAtoms(); ++beg )
        {
            OBAtom* parent = predecessor_map_dfs[*beg];
            if ( parent )
            {
                std::cout << (*beg)->GetIdx() - 1 << " has parent : "
                          << parent->GetIdx() - 1 << "\t";
            }
            else
            {
                std::cout << (*beg)->GetIdx() - 1 << " has no parent\t\t";
            }
            std::cout << " graph theoretical distance : " << distance_map_dfs[*beg] << "\n";
        }

    } // end for loop

    return(1);
}


#include "modules/bio_mapred/compute_coverage.h"
#include "modules/io/bitcount.h"
#include "modules/mapred/resource_manager.h"
#include "modules/bio_base/reference.h"
#include "modules/bio_base/coverage_record.h"
#include "modules/bio_base/seq_position.h"
#include <boost/bind.hpp>

REGISTER_TASK(compute_coverage_task);

void compute_coverage_task::run()
{
	progress_handler_t my_prog = boost::bind(&compute_coverage_task::void_progress, this, _1);
        subprogress sp1(my_prog, 0.0, 0.7);
        subprogress sp2(my_prog, 0.7, 0.75);
        subprogress sp3(my_prog, 0.75, 0.8);
        subprogress sp4(my_prog, 0.8, 1.0);

	::reference ref(reference);
	SPLOG("compute_coverage_task::run> Start");

	size_t one_size = bitcount::compute_size(ref.size());
	mmap_buffer bit_buf;
	resource_manager rm;
	rm.create_resource(bit_buf, one_size*2);

	bitcount bc_uniq(bit_buf.buffer(), ref.size());
	bitcount bc_guess(bit_buf.buffer() + one_size, ref.size());
	bc_uniq.init();
	bc_guess.init();

	manifest_reader mr(input);
	seq_position sp;
	coverage_record cr;
	double tot_recs = input.get_num_records();
	size_t i = 0;
	while(mr.read_msgpack(sp, cr)) {
		sp1(double(i) / tot_recs);
		size_t flat_pos = ref.flatten(sp);
		if (cr.match_count == 1)
			bc_uniq.set(flat_pos, true);
		else
			bc_guess.set(flat_pos, true);
	}
	bc_uniq.finalize(sp2);
	bc_guess.finalize(sp3);

	manifest out;
	rm.write_resource(out, bit_buf, get_root(), "cov", sp4);
	out.metadata().set(meta::ns::readonly, "ref_size", ref.size());
	set_output(out);
	SPLOG("compute_coverage_task::run> Done");
}


//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Alejandro Cabrera 2011.
// Distributed under the Boost
// Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or
// copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/bloom_filter for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_BLOOM_FILTER_BLOOM_FILTER_HPP
#define BOOST_BLOOM_FILTER_BLOOM_FILTER_HPP 1

#include <cmath>
#include <bitset>

#include <boost/config.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/size.hpp>

#include <boost/bloom_filter/detail/apply_hash.hpp>
#include <boost/bloom_filter/hash/default.hpp>

#ifndef BOOST_NO_0X_HDR_INITIALIZER_LIST
#include <initializer_list>
#endif

namespace boost {
  namespace bloom_filters {
    template <typename T,
	      size_t Size,
	      class HashFunctions = mpl::vector<boost_hash<T> > >
    class basic_bloom_filter {
    public:
      typedef T value_type;
      typedef T key_type;
      typedef std::bitset<Size> bitset_type;
      typedef HashFunctions hash_function_type;
      typedef basic_bloom_filter<T, Size,
				 HashFunctions> this_type;

    private:
      typedef detail::apply_hash<mpl::size<HashFunctions>::value - 1,
				 this_type> apply_hash_type;

    public:
      basic_bloom_filter() {}

      template <typename InputIterator>
      basic_bloom_filter(const InputIterator start, const InputIterator end) {
	for (InputIterator i = start; i != end; ++i)
	  this->insert(*i);
      }

#ifndef BOOST_NO_0X_HDR_INITIALIZER_LIST
      basic_bloom_filter(const std::initializer_list<T>& ilist) {
	typedef typename std::initializer_list<T>::const_iterator citer;
	for (citer i = ilist.begin(), end = ilist.end(); i != end; ++i) {
	  this->insert(*i);
	}
      }
#endif

      static BOOST_CONSTEXPR size_t bit_capacity() {
        return Size;
      }

      static BOOST_CONSTEXPR size_t num_hash_functions() {
        return mpl::size<HashFunctions>::value;
      };

      double false_positive_rate() const {
        const double n = static_cast<double>(this->bits.count());
        static const double k = static_cast<double>(num_hash_functions());
        static const double m = static_cast<double>(Size);
        static const double e =
	  2.718281828459045235360287471352662497757247093699959574966;
        return std::pow(1 - std::pow(e, -k * n / m), k);
      };

      size_t count() const {
        return this->bits.count();
      };

      bool empty() const {
	return this->count() == 0;
      }

      const bitset_type&
      data() const
      {
	return this->bits;
      }

      void insert(const T& t) {
        apply_hash_type::insert(t, bits);
      }

      template <typename InputIterator>
      void insert(const InputIterator start, const InputIterator end) {
	for (InputIterator i = start; i != end; ++i) {
	  this->insert(*i);
	}
      }

      bool probably_contains(const T& t) const {
        return apply_hash_type::contains(t, bits);
      }

      void clear() {
        this->bits.reset();
      }

      void swap(basic_bloom_filter& other) {
	basic_bloom_filter tmp = other;
	other = *this;
	*this = tmp;
      }

      basic_bloom_filter& operator|=(const basic_bloom_filter& rhs) {
        this->bits |= rhs.bits;
        return *this;
      }

      basic_bloom_filter& operator&=(const basic_bloom_filter& rhs) {
        this->bits &= rhs.bits;
        return *this;
      }

      template<class _T, size_t _Size, class _HashFunctions>
      friend bool
      operator==(const basic_bloom_filter<_T, _Size, _HashFunctions>&,
		 const basic_bloom_filter<_T, _Size, _HashFunctions>&);

      template<class _T, size_t _Size, class _HashFunctions>
      friend bool
      operator!=(const basic_bloom_filter<_T, _Size, _HashFunctions>&,
		 const basic_bloom_filter<_T, _Size, _HashFunctions>&);

    private:
      bitset_type bits;
    };

    template<class _T, size_t _Size, class _HashFunctions>
    bool
    operator==(const basic_bloom_filter<_T, _Size, _HashFunctions>& lhs,
	       const basic_bloom_filter<_T, _Size, _HashFunctions>& rhs)
    {
      return (lhs.bits == rhs.bits);
    }

    template<class _T, size_t _Size, class _HashFunctions>
    bool
    operator!=(const basic_bloom_filter<_T, _Size, _HashFunctions>& lhs,
	       const basic_bloom_filter<_T, _Size, _HashFunctions>& rhs)
    {
      return !(lhs == rhs);
    }

    template<class _T, size_t _Size, class _HashFunctions>
    basic_bloom_filter<_T, _Size, _HashFunctions>
    operator|(const basic_bloom_filter<_T, _Size, _HashFunctions>& lhs,
	      const basic_bloom_filter<_T, _Size, _HashFunctions>& rhs)
    {
      basic_bloom_filter<_T, _Size, _HashFunctions> ret(lhs);
      ret |= rhs;
      return ret;
    }

    template<class _T, size_t _Size, class _HashFunctions>
    basic_bloom_filter<_T, _Size, _HashFunctions>
    operator&(const basic_bloom_filter<_T, _Size, _HashFunctions>& lhs,
	      const basic_bloom_filter<_T, _Size, _HashFunctions>& rhs)
    {
      basic_bloom_filter<_T, _Size, _HashFunctions> ret(lhs);
      ret &= rhs;
      return ret;
    }

    template<class _T, size_t _Size, class _HashFunctions>
    void
    swap(basic_bloom_filter<_T, _Size, _HashFunctions>& lhs,
	 basic_bloom_filter<_T, _Size, _HashFunctions>& rhs)
    {
      lhs.swap(rhs);
    }
  } // namespace bloom_filters
} // namespace boost
#endif


#include "gtest/gtest.h"

#include "../a2o/utils/geometry/Angle.h"
#include "../a2o/utils/geometry/Pose2D.h"
#include "../a2o/worldmodel/odometry/WheelTickOdometry.h"
#include "../a2o/utils/geometry/drive/DriveCalculator.h"
#include "../a2o/utils/geometry/drive/DriveGeometry.h"

#include <boost/shared_ptr.hpp>

#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Dense>
#include <cmath>

using namespace A2O;
using namespace std;
using namespace Eigen;

namespace {

#define WHEEL_DIAMETER 0.1

class WheelTickOdometryTest: public ::testing::Test {
protected:
  WheelTickOdometryTest() {};
  virtual ~WheelTickOdometryTest() {};

  virtual void SetUp() {
  };
  virtual void TearDown() {
  };
};

TEST_F(WheelTickOdometryTest, update) {
  DriveCalculator::Ptr driveCalculator(boost::make_shared<DriveCalculator>());

  WheelTickOdometry testee(driveCalculator, WHEEL_DIAMETER);
  Pose2D resultPose;
  resultPose = testee.update(0, 0, 0);
//   std::cout << "result: angle: " << resultPose.getAngle().deg() << " x: " << resultPose.x() << " y: " << resultPose.y() << std::endl;
  ASSERT_NEAR(resultPose.x(), 0, 0.000001);
  ASSERT_NEAR(resultPose.y(), 0, 0.000001);
  ASSERT_NEAR(resultPose.getAngle().deg(), 0, 0.000001);

  resultPose = testee.update(4,4, 10);
//   std::cout << "result: angle: " << resultPose.getAngle().deg() << " x: " << resultPose.x() << " y: " << resultPose.y() << std::endl;
  ASSERT_NEAR(resultPose.x(), 0, 0.000001);
  ASSERT_NEAR(resultPose.y(), 0.157079, 0.000001);
  ASSERT_NEAR(resultPose.getAngle().deg(), 0, 0.000001);

  resultPose = testee.update(6,6, 20);
//   std::cout << "result: angle: " << resultPose.getAngle().deg() << " x: " << resultPose.x() << " y: " << resultPose.y() << std::endl;
  ASSERT_NEAR(resultPose.x(), 0, 0.000001);
  ASSERT_NEAR(resultPose.y(), 0.235619, 0.000001);
  ASSERT_NEAR(resultPose.getAngle().deg(), 0, 0.000001);

  resultPose = testee.update(8,8, 30);
//   std::cout << "result: angle: " << resultPose.getAngle().deg() << " x: " << resultPose.x() << " y: " << resultPose.y() << std::endl;
  ASSERT_NEAR(resultPose.x(), 0, 0.000001);
  ASSERT_NEAR(resultPose.y(), 0.314159, 0.000001);
  ASSERT_NEAR(resultPose.getAngle().deg(), 0, 0.000001);

  resultPose = testee.update(11,10, 40);
//   std::cout << "result: angle: " << resultPose.getAngle().deg() << " x: " << resultPose.x() << " y: " << resultPose.y() << std::endl;
  ASSERT_NEAR(resultPose.x(), 0.00739998, 0.000001);
  ASSERT_NEAR(resultPose.y(), 0.411961, 0.000001);
  ASSERT_NEAR(resultPose.getAngle().deg(), -8.653847, 0.000001);

}

TEST_F(WheelTickOdometryTest, testDriveStraight) {
  DriveCalculator::ConstPtr driveCalculator(boost::make_shared<DriveCalculator>(1, 2));
  WheelTickOdometry testee(driveCalculator, WHEEL_DIAMETER);

  testee.update(0, 0, 0);
  testee.update(1, 1, 1000);

  Pose2D result = testee.getCurrentPose();

  ASSERT_NEAR(result.x(), 0, 0.00000001);
  ASSERT_NEAR(result.y(), 0.1 * M_PI / 8, 0.00000001);
  ASSERT_NEAR(result.getAngle().deg(), 0, 0.00000001);
}

TEST_F(WheelTickOdometryTest, testDriveSynchron) {
  DriveCalculator::ConstPtr driveCalculator(boost::make_shared<DriveCalculator>(1, 2));
  WheelTickOdometry testee(driveCalculator, WHEEL_DIAMETER);

  testee.update(0, 0, 0);
  testee.update(0, 1, 1000);
  testee.update(1, 2, 2000);

  Pose2D result = testee.getCurrentPose();
  Pose2D shouldBe = DriveGeometry::calculateCurvePose(0.1 * M_PI / 8, 0.1 * M_PI / 4, 1);

  ASSERT_NEAR(result.x(), shouldBe.x(), 0.00000001);
  ASSERT_NEAR(result.y(), shouldBe.y(), 0.00000001);
  ASSERT_NEAR(result.getAngle().deg(), shouldBe.getAngle().deg(), 0.00000001);
}

TEST_F(WheelTickOdometryTest, testDriveSomehow) {
  DriveCalculator::ConstPtr driveCalculator(boost::make_shared<DriveCalculator>(1, 2));
  WheelTickOdometry testee(driveCalculator, WHEEL_DIAMETER);

  testee.update(0, 0, 0);
  testee.update(0, 1, 1000);
  testee.update(0, 2, 2000);
  testee.update(1, 3, 3000);

  Pose2D result = testee.getCurrentPose();
  Pose2D shouldBe = DriveGeometry::calculateCurvePose(0.1 * M_PI / 8, 3 * 0.1 * M_PI / 8, 1);

  ASSERT_NEAR(result.x(), shouldBe.x(), 0.00000001);
  ASSERT_NEAR(result.y(), shouldBe.y(), 0.00000001);
  ASSERT_NEAR(result.getAngle().deg(), shouldBe.getAngle().deg(), 0.00000001);
}

TEST_F(WheelTickOdometryTest, testDriveSomehow2) {
  DriveCalculator::ConstPtr driveCalculator(boost::make_shared<DriveCalculator>(1, 2));
  WheelTickOdometry testee(driveCalculator, WHEEL_DIAMETER);

  testee.update(0, 0, 0);
  testee.update(1, 0, 1500);
  testee.update(0, 1, 2000);
  testee.update(2, 1, 3000);
  testee.update(0, 2, 4000);
  testee.update(3, 2, 4500);
  testee.update(4, 3, 6000);

  Pose2D result = testee.getCurrentPose();
  Pose2D shouldBe = DriveGeometry::calculateCurvePose(0.1 * M_PI / 2, 3 * 0.1 * M_PI / 8, 1);

  ASSERT_NEAR(result.x(), shouldBe.x(), 0.00000001);
  ASSERT_NEAR(result.y(), shouldBe.y(), 0.00000001);
  ASSERT_NEAR(result.getAngle().deg(), shouldBe.getAngle().deg(), 0.00000001);
}
}