// generate multinomial stuff
#include <iostream>
#include <random>
#include <cmath>
#include <time.h> 

#ifndef LINUX
#include <Accelerate/Accelerate.h>
#endif 
using namespace std;

#ifdef LINUX 
extern "C" {
	//lapack functions
void dgetrf_(int* M, int *N, double* A, int* lda, int* IPIV, int* INFO);
void dgetri_(int* N, double* A, int* lda, int* IPIV, double* WORK, int* lwork, int* INFO);
void dgesv_( int* n, int* nrhs, double* a, int* lda, int* ipiv, double* b, int* ldb, int* info );
void dpotrf_(char *UPLO,int *size1,double *cov,int *size2,int *INFO); 
}
#endif

class GenORs
{
	//An object to generate and store odds ratios drawn from a log-multivariante normal
	public:
		double *cov; //the cholesky decomposition of the covariance matrix in effect sizes (log-ORs)
		double *normsIn; //a vector of generated indendent standard normal variables
		double *ORs; //the generated ORs  
		int size;  //the dimensionality (i.e. number of phenotypes)
		void sampleORs();
		std::default_random_engine generator;
	  	std::normal_distribution<double> rnorm;
		GenORs(double *newCov, int newSize);
};

GenORs::GenORs(double *newCov, int newSize){
	//initialize the object with a given covariance matrix and dimensionality
	
	if (newSize == 0) return;
	generator.seed((rand()));
	std::normal_distribution<double> rnorm(0,1);
	int i, j;
	size = newSize;
	cov = (double *)calloc(size * size, sizeof(double)); 
	normsIn = (double *)calloc(size, sizeof(double)); 
	ORs = (double *)calloc(size, sizeof(double));
	for (i = 0; i < size*size; i++) cov[i] = newCov[i];
	
	int INFO; 
	char UPLO = 'L'; 
	dpotrf_(&UPLO,&size,cov,&size,&INFO); 
	
	for (i = 0; i < size; i++) for (j = 0; j < size; j++){
		if (j < i) cov[j + size*i] = 0;
	}
}

void GenORs::sampleORs(){
	// sample some odds ratios
	
	int i, j;
	for (i = 0; i < size; i++) normsIn[i] = rnorm(generator);
	for (i = 0; i < size; i++) {
		ORs[i] = 0;
		for (j = 0; j < size; j++){
			ORs[i] += cov[i + size*j] * normsIn[j];
		}
		ORs[i] = exp(ORs[i]);
	}

}


