//C++ functions for ETAS

//nneed to find why the gaussian model isnt getting the spatila pparams correct

// add in power law
//spatial sampler ideally needs some way to pass in a kernel density estimate for f(x,y)


#include <R.h>
#include <Rmath.h>
#include "etas.h"
#include <vector>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <random>

using namespace std;

double dpoislog(double k, double lambda);
int sampleDiscrete(std::vector<double> &probs);

//temporal branching sampler
void sampleBranching(std::vector<double> &ts, std::vector<double> &marks, double M0,   double mu, double K, double alpha, double c, double p, std::vector<int> &branching);
double hBranchingPosteriorInteraction(std::vector<double> &ts, std::vector<double> &marks, std::vector<double> &zs, double maxTime, std::vector<double> &kappaevals, double c, double p, double c_lower);
double kappaBranchingPosteriorInteraction(std::vector<double> &ts, std::vector<double> &marks, std::vector<int> &numtriggered, double M0, std::vector<double> &Hevals, double K, double alpha);
void estimateETASBranchingInteraction(std::vector<double> &ts, std::vector<double> &marks, std::vector<int> &branching, double maxTime, double M0, int sims, int numMCMCSamples, double mu, double K, double alpha, double c, double p, double c_lower, std::vector<double> &mus, std::vector<double> &Ks, std::vector<double> &alphas, std::vector<double> &cs, std::vector<double> &ps, int &cp_accept, int &cp_total, int &kappa_accept, int &kappa_total);

//direct MCMC sampler
double ETASlikelihood(std::vector<double> &ts, std::vector<double> &marks, double maxTime, double M0, double mu, double K, double alpha, double c, double p);
double ETASposterior(std::vector<double> &ts, std::vector<double> &marks, double maxTime, double M0, double mu, double K, double alpha, double c, double p);
void estimateETASDirect(std::vector<double> &ts, std::vector<double> &marks, double maxTime, double M0, int sims, double mu, double K, double alpha, double c, double p, std::vector<double> &mus, std::vector<double> &Ks, std::vector<double> &alphas, std::vector<double> &cs, std::vector<double> &ps);

//spatial sampler. Currently only spherical gaussian is implemented
double ETASspatiallikelihood(std::vector<double> &ts, std::vector<double> &marks, std::vector<double> &xs, std::vector<double> ys, double maxTime, double M0, double area, double mu, double K, double alpha, double c, double p, double param1, double param2);
void gBranchingPosteriorGaussian(std::vector<double> &xs, std::vector<double> &ys, std::vector<int> &branching, double &param1, double &param2, double sigma2_prior_shape, double sigma2x_prior_rate, double sigma2y_prior_rate);
void gBranchingPosteriorPowerlaw(std::vector<double> &xs, std::vector<double> &ys, std::vector<int> &branching, double &param1, double &param2);
void sampleSpatialBranching(std::vector<double> &ts, std::vector<double> &marks, double M0, double area, double mu, double K, double alpha, double c, double p, std::vector<int> &branching, std::vector<double> xs, std::vector<double> ys, double param1, double param2, bool gaussian);
void estimateSpatialETASBranching(std::vector<double> &ts, std::vector<double> &marks, std::vector<double> &xs, std::vector<double> ys, std::vector<int> &branching, double maxTime, double M0, double area, int sims, int numMCMCSamples, double mu, double K, double alpha, double c, double p, double c_lower, double param1, double param2, double sigma2_prior_shape, double sigma2x_prior_rate, double sigma2y_prior_rate, std::vector<double> &mus, std::vector<double> &Ks, std::vector<double> &alphas, std::vector<double> &cs, std::vector<double> &ps, std::vector<double> &param1s, std::vector<double> &param2s, bool gaussian, int &cp_accept, int &cp_total, int &kappa_accept, int &kappa_total);

//fast evaluation of log poisson
double dpoislog(double k, double lambda) {
	if (k==0) {
		return(k * log(lambda) - 0 - lambda);
	}
	if (k==1) {
		return(k * log(lambda) - 0 - lambda);
	}
	if (k==2) {
		return(k * log(lambda) - 0.6931472 - lambda);
	}
	if (k==3) {
		return(k * log(lambda) - 1.791759 - lambda);
	}
    return k * log(lambda) - lgamma(k + 1.0) - lambda;
}

int sampleDiscrete(std::vector<double> &probs) {
	double total = 0.0;
	for (std::size_t i = 0; i < probs.size(); i++) {
		if (std::isfinite(probs[i]) && probs[i] > 0) {
			total += probs[i];
		}
	}
	if (!std::isfinite(total) || total <= 0) {
		return(0);
	}
	double u = unif_rand() * total;
	double running = 0.0;
	for (std::size_t i = 0; i < probs.size(); i++) {
		if (std::isfinite(probs[i]) && probs[i] > 0) {
			running += probs[i];
			if (u <= running) {
				return(static_cast<int>(i));
			}
		}
	}
	return(static_cast<int>(probs.size() - 1));
}


//maybe change this to avoid using push_back/clear. Instead, use [] to put elements into vector. See http://stackoverflow.com/questions/20168051/why-push-back-is-slower-than-operator-for-an-previous-allocated-vector
void sampleBranching(std::vector<double> &ts, std::vector<double> &marks, double M0,  double mu, double K, double alpha, double c, double p, std::vector<int> &branching) {
	int n = ts.size();
	int parent;
	double temp;

	branching.clear();
	branching.reserve(n);
	branching.push_back(0);

	std::vector<double> probs;
	probs.reserve(n);

	std::vector<double> kappaevals;
	kappaevals.reserve(n);

	//precompute
	for (int i = 0; i < n; i++) {
		kappaevals.push_back(K*exp(alpha*(marks[i]-M0)));
	}

	for (int i = 1; i < n; i++) {
		probs.clear();
		probs.push_back(mu);

		for (int j = 0; j < i; j++) { //check this iteratures through...
			temp = kappaevals[j]*(p-1)*pow(c,p-1)*pow(ts[i]-ts[j]+c,-p);
			probs.push_back(temp);
		}
		parent = sampleDiscrete(probs);
		branching.push_back(parent);
	}
}


/******** BRANCHING FUNCTIONS WITH INTERACTION (IE WITHOUT INFINITE TIME APPROXIMATION**/
double hBranchingPosteriorInteraction(std::vector<double> &ts, std::vector<double> &marks, std::vector<double> &z, double maxTime, std::vector<double> &kappaevals, double c, double p, double c_lower) {
	(void) marks;
	if (c <= c_lower || p <= 1 || c > 10 || p > 10) {
		return(-9999999);
	}
	//cout << c << " " << p << "\n";

	double loglik = 0, temp;
	int n = ts.size();
	for (int i = 0; i < n; i++) {
		//temp = K*exp(alpha*(marks[i]-M0));
		temp = 1 - (pow(c,p-1) / pow(maxTime-ts[i]+c,p-1)); //H
		loglik = loglik - kappaevals[i]*temp;
	}
	for (std::size_t i = 0; i < z.size(); i++) {
		loglik += log(p-1) + (p-1)*log(c) - p*log(z[i]+c);
	}
	return(loglik);
}

double kappaBranchingPosteriorInteraction(std::vector<double> &ts, std::vector<double> &marks, std::vector<int> &numtriggered, double M0, std::vector<double> &Hevals, double K,  double alpha) {
	if (alpha < 0 || alpha > 10|| K < 0 || K > 10) {
		return(-9999999);
	}
	//double K = exp(K);
	double loglik = 0, temp;
	int n = ts.size();
	for (int i = 0; i < n; i++) {
		temp = K*exp(alpha*(marks[i]-M0));
		//temp2 = 1 - (pow(c,p-1) / pow(maxTime-ts[i]+c,p-1)); //H
		loglik = loglik - temp*Hevals[i];
		loglik += numtriggered[i] * log(temp);
	}
	return(loglik);
}


void estimateETASBranchingInteraction(std::vector<double> &ts, std::vector<double> &marks, std::vector<int> &branching, double maxTime, double M0, int sims, int numMCMCSamples, double mu, double K, double alpha, double c, double p, double c_lower, std::vector<double> &mus, std::vector<double> &Ks, std::vector<double> &alphas, std::vector<double> &cs, std::vector<double> &ps, int &cp_accept, int &cp_total, int &kappa_accept, int &kappa_total) {
	int n = ts.size(), numbackground;
	double currposterior,newposterior, newc,newp,newK,newalpha, etac, etap, newetac, newetap;
	double mualpha = 0.1, mubeta = 0.1; //prior params
	std::vector<double> kappaevals;
	kappaevals.reserve(n);
	std::vector<double> Hevals;
	Hevals.reserve(n);
	std::vector<double> z;
	z.reserve(n);
	for (int s=0; s<sims; s++) {

		//cout << "sampling branching\n";
		sampleBranching(ts,marks,M0,mu,K,alpha,c,p,branching);


		//cout << "generating z\n";
		z.clear();
		std::vector<int> numtriggered(n,0);
		numbackground = 0;
		for (int i=0; i<n; i++) {
			if (branching[i] > 0) {
				numtriggered[branching[i]-1]++; //worry aobut indexing here...
				z.push_back(ts[i]-ts[branching[i]-1]);
			} else {
				numbackground++;
			}
		}


		//cout << "sampling mu\n";
		mu = rgamma(mualpha+numbackground, 1/(mubeta+maxTime));
		mus.push_back(mu);


		//cout << "generating kappa\n";
		kappaevals.clear();
		for (int i=0; i<n; i++) {
			kappaevals.push_back(K*exp(alpha*(marks[i]-M0)));
		}


		//cout << "c p\n";
		//try sampling c and p seperately, not together...
		currposterior = hBranchingPosteriorInteraction(ts,marks,z,maxTime,kappaevals,c,p,c_lower) + log(c-c_lower) + log(p-1);
		for (int i=0; i < numMCMCSamples; i++) {
			etac = log(c-c_lower);
			etap = log(p-1);
			newetac = etac + rnorm(0, 0.25);
			newetap = etap + rnorm(0, 0.15);
			newc = c_lower + exp(newetac);
			newp = 1 + exp(newetap);
			newposterior = hBranchingPosteriorInteraction(ts,marks,z,maxTime,kappaevals,newc,newp,c_lower) + log(newc-c_lower) + log(newp-1);
			cp_total++;
			if (unif_rand() < exp(newposterior-currposterior)) {
				c = newc;
				p = newp;
				currposterior = newposterior;
				cp_accept++;
			}
		}
		cs.push_back(c);
		ps.push_back(p);


		//cout << "generating H\n";
		Hevals.clear();
		for (int i=0; i<n; i++) {
			Hevals.push_back(1 - (pow(c,p-1) / pow(maxTime-ts[i]+c,p-1)));
		}


		//cout << "K alpha\n";
		currposterior = kappaBranchingPosteriorInteraction(ts,marks,numtriggered,M0,Hevals,K,alpha);
		for (int i=0; i < 100; i++) {
			newK = K + rnorm(0, 0.1);
			newalpha = alpha + rnorm(0, 0.1);
			newposterior = kappaBranchingPosteriorInteraction(ts,marks,numtriggered,M0,Hevals,newK,newalpha);
			kappa_total++;
			if (unif_rand() < exp(newposterior-currposterior)) {
				K = newK;
				alpha = newalpha;
				currposterior = newposterior;
				kappa_accept++;
			}
		}
		Ks.push_back(K);
		alphas.push_back(alpha);

		if (s % 100 == 0) {
		  Rprintf("Generated %d/%d samples\n",s,sims);
			//cout << "Generated " << s << "/" << sims << " samples\n";
		}
	}
}




/************** DIRECT FUNCTIONS THAT DONT USE BRANCHING***********/
//DOENST HAVE MU PRIOR IN YET, NEED DGAMMA
double ETASlikelihood(std::vector<double> &ts, std::vector<double> &marks, double maxTime, double M0, double mu, double K, double alpha, double c, double p) {
	double part1, part2, part3, temp;
	int n = ts.size();
	//cout << maxTime << " " << M0 << " " << mu <<  " " << K  << " " << alpha << " " << c << " " << p << " " << ts[0] << " "  << marks[0]  << " " << ts[n-1] << " " << marks[n-1] << " " << n << "\n";

	part1 = log(mu); //this is lambda(ts_1)
	for (int i = 1; i < n; i++) {
		temp = mu;
		for (int j = 0; j < i; j++) {
			temp +=  ((p-1)*pow(c,p-1)*K*exp(alpha*(marks[j]-M0)))/pow(ts[i]-ts[j]+c,p);
		}
		//cout << temp << "\n";
		part1 += log(temp);
	}

	part2 = mu*maxTime;

	part3 = 0;
	for (int i = 0; i<n; i++) {
		temp = (1 - pow(c,p-1)/pow(maxTime-ts[i]+c,p-1));
		part3 += K*exp(alpha*(marks[i]-M0))*temp;
	}

	//cout << part1 << " " << part2 << " " << part3 << "\n";
	return(part1 - part2 - part3);
}



double ETASposterior(std::vector<double> &ts, std::vector<double> &marks, double maxTime, double M0, double mu, double K, double alpha, double c, double p) {
	if (c<0 || p<1 || c>10 || p>10 || alpha <0 || alpha >10 || mu < 0 || mu > 10 || K < 0 || K >10) {
		return(-999999);
	} else {
		return(ETASlikelihood(ts, marks, maxTime, M0, mu, K, alpha, c, p));
	}
}







void estimateETASDirect(std::vector<double> &ts, std::vector<double> &marks, double maxTime, double M0, int sims, double mu, double K, double alpha, double c, double p, std::vector<double> &mus, std::vector<double> &Ks, std::vector<double> &alphas, std::vector<double> &cs, std::vector<double> &ps) {
	double currposterior,newposterior, newmu,newc,newp,newK,newalpha;

    currposterior = ETASposterior(ts, marks, maxTime, M0, mu, K, alpha, c, p);

	 //K = exp(K);

	for (int s=0; s<sims; s++) {
		newmu = mu + rnorm(0, 0.05);
		newposterior = ETASposterior(ts, marks, maxTime, M0, newmu, K, alpha, c, p);
		if (unif_rand() < exp(newposterior-currposterior)) {
			mu = newmu;
			currposterior = newposterior;
		}
		mus.push_back(mu);

		newK = K + rnorm(0, 0.15);
		newposterior = ETASposterior(ts, marks, maxTime, M0, mu, newK, alpha, c, p);
		if (unif_rand() < exp(newposterior-currposterior)) { //need these extra log terms since its the transformation jacobian, see
			K = newK;
			currposterior = newposterior;
		}
		Ks.push_back(log(K));

		newalpha = alpha + rnorm(0, 0.15);
		newposterior = ETASposterior(ts, marks, maxTime, M0, mu, K, newalpha, c, p);
		if (unif_rand() < exp(newposterior-currposterior)) {
			alpha = newalpha;
			currposterior = newposterior;
		}
		alphas.push_back(alpha);

		newc = c + rnorm(0, 0.25);
		newp = p + rnorm(0, 0.30);
		newposterior = ETASposterior(ts, marks, maxTime, M0, mu, K, alpha, newc, newp);
		if (unif_rand() < exp(newposterior-currposterior)) {
			c = newc;
		  p = newp;
			currposterior = newposterior;
		}
		cs.push_back(c);
		ps.push_back(p);


		if (s % 1 == 0) {
			Rprintf("Generated %d/%d samples\n",s,sims);
			//cout << "Generated " << s << "/" << sims << " samples\n";
		}
	}
}


/******* SPATIAL FUNCTIONS *****/
//everything assumes constant. mu and a triggering space kernel that is either:
//a) gaussian with 2 params param1 and param2 (ie sphereical)
//b) powerlaw with params d and q
//these are referred to in the code as param1 (param1, d) and param2 (sigmawy, q)

//only for gaussian
double ETASspatiallikelihood(std::vector<double> &ts, std::vector<double> &marks, std::vector<double> &xs, std::vector<double> ys, double maxTime, double M0, double area, double mu, double K, double alpha, double c, double p, double param1, double param2) {
	double part1, part2, part3, temp, temp2, temp3;
	int n = ts.size();

	//cout << maxTime << " " << M0 << " " << mu <<  " " << K  << " " << alpha << " " << c << " " << p << " " << "\n";




	part1 = log(mu/area);
	for (int i = 1; i < n; i++) {
		temp = mu/area; //this gets divided by area
		for (int j = 0; j < i; j++) {

			temp2 = xs[i]-xs[j];
			temp3 = ys[i]-ys[j];
			temp +=  ((p-1)*pow(c,p-1)*K*exp(alpha*(marks[j]-M0)))/pow(ts[i]-ts[j]+c,p) * 1/(2.0*3.141592*sqrt(param1)*sqrt(param2)) * exp(- (temp2*temp2)/(2*param1) - (temp3*temp3)/(2*param2));
		}
		part1 += log(temp);
	}

	part2 = mu*maxTime; //i dont think this is divided by area, since its integrated out

	part3 = 0;
	for (int i = 0; i<n; i++) {
		temp = (1 - pow(c,p-1)/pow(maxTime-ts[i]+c,p-1));
		part3 += K*exp(alpha*(marks[i]-M0))*temp;
	}

	//cout << part1 << " " << part2 << " " << part3 << "\n";
	//double prior=0;
	return( part1 - part2 - part3);
}




void gBranchingPosteriorGaussian(std::vector<double> &xs, std::vector<double> &ys, std::vector<int> &branching, double &param1, double &param2, double sigma2_prior_shape, double sigma2x_prior_rate, double sigma2y_prior_rate) {
	double temp;
	double alpha=sigma2_prior_shape, betax=sigma2x_prior_rate, betay=sigma2y_prior_rate;

	int n = xs.size();

	for (int i = 0; i < n; i++) {
		if (branching[i] == 0) {
			continue;
		}
		alpha += 0.5;
		temp = xs[i] - xs[branching[i]-1];
		betax += 0.5*temp*temp;
		temp = ys[i] - ys[branching[i]-1];
		betay += 0.5*temp*temp;
	}

	//check gamma parameterisatoin is right
	param1 =  1.0/rgamma(alpha,1.0/betax);
	param2 = 1.0/rgamma(alpha,1.0/betay);

}


//assumes gamma(alpha,beta) prior on both params
void gBranchingPosteriorPowerlaw(std::vector<double> &xs, std::vector<double> &ys, std::vector<int> &branching, double &param1, double &param2) {
	(void) xs;
	(void) ys;
	(void) branching;
	(void) param1;
	(void) param2;
	return;
	/*double alpha=1, beta=1;
	int numMCMCSamples=100;
    std::random_device rd;
    std::mt19937 gen(rd());

	int n = xs.size();

	currposterior = gPowerlawPosterior(ts,marks,z,maxTime,kappaevals,newc,newp);
	for (int i=0; i < numMCMCSamples; i++) {
		newparam1 = param1 + rnorm(gen);
		newparam2 = param2 + rnorm(gen);
		newposterior = gPowerlawPosterior(ts,marks,z,maxTime,kappaevals,newc,newp);
		if (runif(gen) < exp(newposterior-currposterior)) {
			param1 = newparam1;
			param2 = newparam2;
			currposterior = newposterior;
		}
	}
*/}





void sampleSpatialBranching(std::vector<double> &ts, std::vector<double> &marks, double M0, double area,  double mu, double K, double alpha, double c, double p, std::vector<int> &branching, std::vector<double> xs, std::vector<double> ys, double param1, double param2, bool gaussian) {
    //cout << "gaissian\n";

	int n = ts.size();
	int parent;
	double temp, temp2, temp3;
	//double K = exp(K);
	double normalisingconstant;

	branching.clear();
	branching.reserve(n);
	branching.push_back(0);

	std::vector<double> probs;
	probs.reserve(n);

	std::vector<double> kappaevals;
	kappaevals.reserve(n);


	//precompute
	for (int i = 0; i < n; i++) {
		kappaevals.push_back(K*exp(alpha*(marks[i]-M0)));
	}
	if (gaussian==true) {
		normalisingconstant = 1.0/(2.0*3.141592*sqrt(param1)*sqrt(param2));
	} else {
		normalisingconstant = (param2-1)*pow(param1, param2-1)/3.141592;
	}

	for (int i = 1; i < n; i++) {
		probs.clear();
		probs.push_back(mu/area); //need to divide by area here i think?

		for (int j = 0; j < i; j++) { //check this iteratures through...
			temp = kappaevals[j]*(p-1)*pow(c,p-1)*pow(ts[i]-ts[j]+c,-p);
			temp = temp*normalisingconstant;
			temp2 = xs[i]-xs[j];
			temp3 = ys[i]-ys[j];
			//if (gaussian == true) {
			temp = temp * exp(- (temp2*temp2)/(2*param1) - (temp3*temp3)/(2*param2));
			//} else {
				//temp = temp * pow(temp2+temp3+param1,-param2);
			//}
			probs.push_back(temp);
		}

		parent = sampleDiscrete(probs);
		branching.push_back(parent);
	}
}


//chekd we parameterised gamma correctly in spatial
void estimateSpatialETASBranching(std::vector<double> &ts, std::vector<double> &marks, std::vector<double> &xs, std::vector<double> ys, std::vector<int> &branching, double maxTime, double M0, double area, int sims, int numMCMCSamples, double mu, double K, double alpha, double c, double p, double c_lower, double param1, double param2, double sigma2_prior_shape, double sigma2x_prior_rate, double sigma2y_prior_rate, std::vector<double> &mus, std::vector<double> &Ks, std::vector<double> &alphas, std::vector<double> &cs, std::vector<double> &ps, std::vector<double> &param1s, std::vector<double> &param2s, bool gaussian, int &cp_accept, int &cp_total, int &kappa_accept, int &kappa_total) {
	int n = ts.size(), numbackground;
	double currposterior,newposterior, newc,newp,newK,newalpha, etac, etap, newetac, newetap;
	double mualpha = 0.1, mubeta = 0.1; //prior params
	std::vector<double> kappaevals;
	kappaevals.reserve(n);
	std::vector<double> Hevals;
	Hevals.reserve(n);
	std::vector<double> z;
	z.reserve(n);
    //double K;
	for (int s=0; s<sims; s++) {


		sampleSpatialBranching(ts,marks,M0,area,mu,K,alpha,c,p,branching,xs,ys,param1,param2,gaussian);


		z.clear();
		std::vector<int> numtriggered(n,0);
		numbackground = 0;
		for (int i=0; i<n; i++) {
			if (branching[i] > 0) {
				numtriggered[branching[i]-1]++; //worry aobut indexing here...
				z.push_back(ts[i]-ts[branching[i]-1]);
			} else {
				numbackground++;
			}
		}

		mu = rgamma(mualpha+numbackground, 1/(mubeta+maxTime));
		//cout << numbackground << " " << mubeta+maxTime << " " << mu<< "\n";

		kappaevals.clear();
		for (int i=0; i<n; i++) {
			kappaevals.push_back(K*exp(alpha*(marks[i]-M0)));
		}


		//try sampling c and p seperately, not together...
		currposterior = hBranchingPosteriorInteraction(ts,marks,z,maxTime,kappaevals,c,p,c_lower) + log(c-c_lower) + log(p-1);
		for (int i=0; i < numMCMCSamples; i++) {
			etac = log(c-c_lower);
			etap = log(p-1);
			newetac = etac + rnorm(0, 0.25);
			newetap = etap + rnorm(0, 0.15);
			newc = c_lower + exp(newetac);
			newp = 1 + exp(newetap);
			newposterior = hBranchingPosteriorInteraction(ts,marks,z,maxTime,kappaevals,newc,newp,c_lower) + log(newc-c_lower) + log(newp-1);
			cp_total++;
			if (unif_rand() < exp(newposterior-currposterior)) {
				c = newc;
				p = newp;
				currposterior = newposterior;
				cp_accept++;
			}
		}

		Hevals.clear();
		for (int i=0; i<n; i++) {
			Hevals.push_back(1 - (pow(c,p-1) / pow(maxTime-ts[i]+c,p-1)));
		}

		currposterior = kappaBranchingPosteriorInteraction(ts,marks,numtriggered,M0,Hevals,K,alpha);
		for (int i=0; i < 100; i++) {
			newK = K + rnorm(0, 0.1);
			newalpha = alpha + rnorm(0, 0.1);
			newposterior = kappaBranchingPosteriorInteraction(ts,marks,numtriggered,M0,Hevals,newK,newalpha);
			kappa_total++;
			if (unif_rand() < exp(newposterior-currposterior)) {
				K = newK;
				alpha = newalpha;
				currposterior = newposterior;
				kappa_accept++;
			}
		}

		mus.push_back(mu);
		cs.push_back(c);
		ps.push_back(p);
		Ks.push_back(K);
		alphas.push_back(alpha);

		/*sample spatial*/
		if (gaussian==true) {
			gBranchingPosteriorGaussian(xs, ys, branching, param1, param2, sigma2_prior_shape, sigma2x_prior_rate, sigma2y_prior_rate);
		} else {
			gBranchingPosteriorPowerlaw(xs, ys, branching, param1, param2);
		}

		param1s.push_back(param1);
		param2s.push_back(param2);



		if (s % 100 == 0) {
		  Rprintf("Generated %d/%d samples\n",s,sims);
		  //cout << "Generated " << s << "/" << sims << " samples\n";
		}
	}
}




extern "C" {
	void estimateETASBranchingC(double *ts, double *marks, int *branching, int *n, double *maxTime, double *M0, int *sims, int *numMCMCSamples, double *mu, double *K, double *alpha, double *c, double *p, double *c_lower, double *mus, double *Ks, double *alphas, double *cs, double *ps, int *cp_accept, int *cp_total, int *kappa_accept, int *kappa_total) {
		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);
		std::vector<int> vbranching(branching,branching + *n);

   	 	std::vector<double> vmus;
    	std::vector<double> vKs;
    	std::vector<double> valphas;
   	 	std::vector<double> vcs;
    	std::vector<double> vps;
    	vmus.reserve(*sims);
    	vKs.reserve(*sims);
    	valphas.reserve(*sims);
    	vcs.reserve(*sims);
    	vps.reserve(*sims);

		int cpacc = 0, cptot = 0, kacc = 0, ktot = 0;
		GetRNGstate();
		estimateETASBranchingInteraction(vts, vmarks, vbranching, *maxTime, *M0,*sims, *numMCMCSamples, *mu, *K, *alpha, *c, *p, *c_lower, vmus, vKs, valphas, vcs, vps, cpacc, cptot, kacc, ktot);
		PutRNGstate();

		std::copy(vmus.begin(),vmus.end(),mus);
    std::copy(vKs.begin(),vKs.end(),Ks);
    std::copy(valphas.begin(),valphas.end(),alphas);
		std::copy(vcs.begin(),vcs.end(),cs);
    std::copy(vps.begin(),vps.end(),ps);
		*cp_accept = cpacc;
		*cp_total = cptot;
		*kappa_accept = kacc;
		*kappa_total = ktot;
		return;
	}

	/* always uses interacting tmeporal sampler*/
	void estimateSpatialETASBranchingC(double *ts, double *marks, double *xs, double *ys, int *branching, int *n, double *maxTime, double *M0, double *area, int *sims, int *numMCMCSamples, double *mu, double *K, double *alpha, double *c, double *p, double *c_lower, double *param1, double *param2, double *sigma2_prior_shape, double *sigma2x_prior_rate, double *sigma2y_prior_rate, double *mus, double *Ks, double *alphas, double *cs, double *ps, double *param1s, double *param2s, int *cp_accept, int *cp_total, int *kappa_accept, int *kappa_total, int *gaussian) {
		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);
		std::vector<int> vbranching(branching,branching + *n);
		std::vector<double> vxs(xs,xs + *n);
		std::vector<double> vys(ys,ys + *n);
		bool use_gaussian = (*gaussian != 0);

   	 	std::vector<double> vmus;
    	std::vector<double> vKs;
    	std::vector<double> valphas;
   	 	std::vector<double> vcs;
    	std::vector<double> vps;
    	std::vector<double> vparam1s;
    	std::vector<double> vparam2s;

    	vmus.reserve(*sims);
    	vKs.reserve(*sims);
    	valphas.reserve(*sims);
    	vcs.reserve(*sims);
    	vps.reserve(*sims);
    	vparam1s.reserve(*sims);
    	vparam2s.reserve(*sims);

		int cpacc = 0, cptot = 0, kacc = 0, ktot = 0;
		GetRNGstate();
		estimateSpatialETASBranching(vts, vmarks, vxs, vys, vbranching, *maxTime, *M0, *area, *sims, *numMCMCSamples, *mu, *K, *alpha, *c, *p, *c_lower, *param1, *param2, *sigma2_prior_shape, *sigma2x_prior_rate, *sigma2y_prior_rate, vmus, vKs, valphas, vcs, vps, vparam1s, vparam2s, use_gaussian, cpacc, cptot, kacc, ktot);
		PutRNGstate();

		std::copy(vmus.begin(),vmus.end(),mus);
    std::copy(vKs.begin(),vKs.end(),Ks);
    std::copy(valphas.begin(),valphas.end(),alphas);
		std::copy(vcs.begin(),vcs.end(),cs);
    std::copy(vps.begin(),vps.end(),ps);
		std::copy(vparam1s.begin(),vparam1s.end(), param1s);
    std::copy(vparam2s.begin(),vparam2s.end(), param2s);
		*cp_accept = cpacc;
		*cp_total = cptot;
		*kappa_accept = kacc;
		*kappa_total = ktot;


		return;
	}


	void estimateETASDirectC(double *ts, double *marks, int *n, double *maxTime, double *M0, int *sims, double *mu, double *K, double *alpha, double *c, double *p, double *mus, double *Ks, double *alphas, double *cs, double *ps) {
		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);

   	 	std::vector<double> vmus;
    	std::vector<double> vKs;
    	std::vector<double> valphas;
   	 	std::vector<double> vcs;
    	std::vector<double> vps;
    	vmus.reserve(*sims);
    	vKs.reserve(*sims);
    	valphas.reserve(*sims);
    	vcs.reserve(*sims);
    	vps.reserve(*sims);

		GetRNGstate();
		estimateETASDirect(vts, vmarks, *maxTime, *M0,*sims, *mu, *K, *alpha, *c, *p, vmus, vKs, valphas, vcs, vps);
		PutRNGstate();

		std::copy(vmus.begin(),vmus.end(),mus);
        std::copy(vKs.begin(),vKs.end(),Ks);
        std::copy(valphas.begin(),valphas.end(),alphas);
		std::copy(vcs.begin(),vcs.end(),cs);
        std::copy(vps.begin(),vps.end(),ps);
		return;
	}

	void estimateBranchingC(double *ts, double *marks, int *n, double *M0, int *sims, double *mu, double *K, double *alpha, double *c, double *p, int *branchings) {
		std::vector<int> vbranching;
		std::vector<int> vbranchings;
		vbranching.reserve(*n);
		vbranchings.reserve(*n * *sims);

		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);

		GetRNGstate();
		for (int i = 0; i < *sims; i++) {
			sampleBranching(vts, vmarks, *M0, *mu, *K, *alpha, *c, *p, vbranching);

			for (int j = 0; j < *n; j++) {
				vbranchings.push_back(vbranching[j]);
			}
		}
		PutRNGstate();
  std::copy(vbranchings.begin(),vbranchings.end(),branchings);
		return;
	}


	void estimateSpatialBranchingC(double *ts, double *marks, double *xs, double *ys, int *n, double *M0, double *area, int *sims, double *mu, double *K, double *alpha, double *c, double *p, double *param1, double *param2, int *branchings, int *gaussian) {
		std::vector<int> vbranching;
		std::vector<int> vbranchings;
		vbranching.reserve(*n);
		vbranchings.reserve(*n * *sims);
		bool use_gaussian = (*gaussian != 0);

		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);
		std::vector<double> vxs(xs,xs + *n);
		std::vector<double> vys(ys,ys + *n);

		//cout << *gaussian << "\n";
		GetRNGstate();
		for (int i = 0; i < *sims; i++) {
			sampleSpatialBranching(vts, vmarks, *M0, *area, *mu, *K, *alpha, *c, *p, vbranching,  vxs, vys, *param1, *param2, use_gaussian);
			for (int j = 0; j < *n; j++) {
				vbranchings.push_back(vbranching[j]);
			 }
	  }
		PutRNGstate();
  std::copy(vbranchings.begin(),vbranchings.end(),branchings);
		return;
	}



	void ETASlikelihoodC(double *ts, double *marks, int *n, double *maxTime, double *M0, double *mu, double *K, double *alpha, double *c, double *p, double *loglik) {
		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);

		*loglik = ETASlikelihood(vts, vmarks, *maxTime, *M0, *mu, *K, *alpha, *c, *p);
	}


	void ETASspatiallikelihoodC(double *ts, double *marks, double *xs, double *ys, int *n, double *maxTime, double *M0, double *area, double *mu, double *K, double *alpha, double *c, double *p, double *param1, double *param2, double *loglik) {
		std::vector<double> vts(ts,ts + *n);
		std::vector<double> vmarks(marks,marks + *n);
		std::vector<double> vxs(xs,xs + *n);
		std::vector<double> vys(ys,ys + *n);


		*loglik = ETASspatiallikelihood(vts, vmarks, vxs, vys, *maxTime, *M0, *area, *mu, *K, *alpha, *c, *p, *param1, *param2);
	}

}
