/*  trinculo, Flexible tools for multinomial association
    Copyright (C) 2014  Luke Jostins

    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, either version 3 of the License, or
    (at your option) any later version.

    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.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

#define PI 3.141592 // this defines PI to be 3.141592

#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <sstream>
#include <iterator>
#include <fstream>
#include <stdlib.h>
#include "readBed.h"
#include "readDosage.h"
#include "readCovar.h"
#include "readPriors.h"
#include "readPhenos.h"
#include "MultinomNewton.h"
#include "PropOddsNewton.h"
#include "chisq_cdf.h"
#include "inputparser.h"
#include "EstCovMat.h"
#include "trinculo.h"
#include <iomanip>
#include <string>
#include <stdio.h>
#include <time.h>
#include <pthread.h>

// little function for converting things to strings
#define SSTR( x ) dynamic_cast< std::ostringstream & >( \
        ( std::ostringstream() << std::dec << x ) ).str()

using namespace std;

//little function to get the current date and time
const std::string currentDateTime() {
    time_t     now = time(0);
    struct tm  tstruct;
    char       buf[80];
    tstruct = *localtime(&now);
    // Visit http://en.cppreference.com/w/cpp/chrono/c/strftime
    // for more information about date/time format
    strftime(buf, sizeof(buf), "%Y-%m-%d %X", &tstruct);

    return buf;
}

void *runMT(void * t){
	MultinomThread *MT;
	MT = (MultinomThread *) t;
	MT->RunTests();	
	pthread_exit(NULL);
}

EstCovMat inferCovar(inputs *opts, bedfile *bed, dosagefile *dosage, covars *cov, phenos *phen, vector<int> conditionsnps){
  // this function gathers all the data needed to infer an empirical covariance matrix (i.e. effect sizes and error matrices at each variant)

  int i, j, l;
  
  //set the phenotype that we are analysing
  int phenfocus = -1;
  for (i = 0; i < phen->Nphenos; i++){
    if (phen->phenonames[i] == opts->phenoname){
      phenfocus = i;
    }
  }
  if (phenfocus == -1){
    cout << "Error: Phenotype " << opts->phenoname << " not found in phenotype file." << endl;
    throw(2);
  }
  
  //set up parameters from the opts object
  int Npred = 1;
  if (opts->covarfile != "") Npred += cov->Ncovars;
  if (opts->conditionfile != "") Npred += conditionsnps.size();
  int Npheno = phen->phenocounts[phenfocus] - 1;
  int Nind;
  int Nvars;
  if (opts->dosagefile == ""){
	Nind = bed->Ninds;
	Nvars = bed->Nvars;
  } else {
    Nind = dosage->Ninds;
    Nvars = dosage->Nvars;
  }
  
  // initialize the model fitting object
   MultinomNewton NR(Npred, Nind, Npheno);
  
  // set the confounders and SNPs to conditon on
  if (opts->covarfile != "") NR.setCovars(cov);
  if (opts->conditionfile != ""){
    if (opts->dosagefile == "") {
		NR.setConditional(conditionsnps, bed);
	} else {
		NR.setConditional(conditionsnps, dosage);
	}
  }
  NR.setPheno(phen,phenfocus); // set the phenotypes
  
  
  // set up some objects for holdign summary statistics
  double * betas = new double [Npheno];
  double * errs = new double [Npheno*Npheno];
  EstCovMat CovLearner(Npheno);
  
  // somet things that aren't used but the objects expect
  int *constrain = new int[Npheno *(1 + Npred)];
  for (i = 0; i < Npheno*(Npred + 1); i++) constrain[i] = 0;
  double Lalt;

  //fit the logistic model on each SNP
  int snp;
  int found;
  for (snp = 0; snp < Nvars; snp++){
     
	  //do not collet data on SNPs that have been conditioned on
     found=0;
     for (i = 0; i < conditionsnps.size(); i++){
		 if (conditionsnps[i] == snp) found=1;
     }
     if (found){
		 continue;	
     }
	 
	 // set the genotypes at this SNP
     if (opts->dosagefile == "")
       NR.setGenos(bed,snp);
     else
       NR.setDosage(dosage,snp);
    
	 //Fit the model and get the error matrix
     NR.setMissing();
     NR.zeroBetas();
     NR.fitLogistic(constrain);
	 NR.calcI();
	 inverse(NR.I,(Npred+1)*Npheno);
	 
	 //extract out effect sizes and errors, and add them to the object
	 for (i = 0; i < Npheno; i++){
		 betas[i] = NR.beta[1 + i*(Npred + 1)];
		 for (j = 0; j < Npheno; j++){
			 errs[i + Npheno*j] = NR.I[1 + i*(Npred + 1) + Npheno*(Npred + 1)*(1 + j*(Npred + 1))];
		 }
	 }
	 CovLearner.AddLocus(betas,errs);

   }
   return(CovLearner);
}


int runPropOdds(inputs *opts, bedfile *bed, dosagefile *dosage, covars *cov, phenos *phen, vector<int> conditionsnps){
  // fit the proporional odds model
	
  int i, j;
  double precision;
  
  //set the phenotype that we are analysing
  int phenfocus = -1;
  for (i = 0; i < phen->Nphenos; i++){
    if (phen->phenonames[i] == opts->phenoname){
      phenfocus = i;
    }
  }
  if (phenfocus == -1){
    cout << "Error: Phenotype " << opts->phenoname << " not found in phenotype file." << endl;
    throw(2);
  }
  
  //set parameters of the model from opts object
  int Npred = 1;
  if (opts->covarfile != "") Npred += cov->Ncovars;
  int Npheno = phen->phenocounts[phenfocus] - 1;
  int Nind;
  int Nvars;
  if (opts->dosagefile == ""){
    Nind = bed->Ninds;
    Nvars = bed->Nvars;
  } else {
    Nind = dosage->Ninds;
    Nvars = dosage->Nvars;
  }
  

  //initialize the model fitting object
  PropOddsNewton NR(Npred, Nind, Npheno);
  
  // initialize the output stream
  ofstream outfile;
  outfile.open((opts->outprefix + ".assoc.ordinal").c_str());
  
  //set phenotypes and confounders
  if (opts->covarfile != "") NR.setCovars(cov);
  NR.setPheno(phen,phenfocus);
  
  // print header
  if (opts->dosagefile == "")
    outfile << "RSID\tA1\tA2\tPROPODDS\tL0\tL1\tPVAL" << endl;
  else 
    outfile << "RSID\tPROPODDS\tL0\tL1\tPVAL" << endl;
  
  // fit the model for eah SNP
  cout << "Carrying out association testing on " << Nvars << " SNPs." << endl;
  int * constrain = new int[Npheno + Npred];
  int snp;
  for (snp = 0; snp < Nvars; snp++){

	  // set the genotypes
    if (opts->dosagefile == "")
      NR.setGenos(bed,snp);
    else
      NR.setDosage(dosage,snp);
    
	// find missing data and 
    NR.setMissing();
    NR.zeroBetas();
	
	// fit the null model (i.e. where the parameter is constrained)
    for (i = 0; i < (Npheno + Npred); i++) constrain[i] = 0;
    constrain[Npheno] = 1;
    double Lold;
    precision = NR.fitLogistic(constrain,opts->fititer,opts->fitreltol,opts->fitabstol);
    if (precision > 1e-5){
      if (opts->dosagefile == "")
	cout << "Warning: Null model did not coverge for SNP " << bed->RSID[snp] << endl;
      else
	cout << "Warning: Null model did not coverge for SNP " << dosage->RSID[snp] << endl;
    }
    Lold = NR.calcL();
	
	// fit the alt model
    constrain[Npheno] = 0;    
    precision = NR.fitLogistic(constrain,opts->fititer,opts->fitreltol,opts->fitabstol);
    if (precision > 1e-5){
      if (opts->dosagefile == "")
		  cout << "Warning: Alt model did not coverge for SNP " << bed->RSID[snp] << endl;
      else
		  cout << "Warning: Alt model did not coverge for SNP " << dosage->RSID[snp] << endl;
    }
	
	//write the output
    if (opts->dosagefile == "")
      outfile << bed->RSID[snp] << "\t" << bed->A1[snp] << "\t" << bed->A2[snp];
    else
      outfile << dosage->RSID[snp];
    
    outfile << "\t" << setprecision(opts->precision) << fixed << exp(NR.beta[Npheno]);
    outfile << "\t" << NR.calcL() << "\t" << Lold;
    double p = pchisq(2*(NR.calcL() - Lold),1); //calculate p-value by LRT
    if (p < 1e-3){
      outfile << "\t" << setprecision(opts->precision) << scientific << p << endl;
    } else {
      outfile << "\t" << setprecision(opts->precision) << fixed << p << endl;
    }
  }
  return(0);
}



int runMultinom(inputs *opts, bedfile *bed, dosagefile *dosage, covars *cov, phenos *phen, vector<int> conditionsnps){
  // fit the multinomial model
  	
  int i, j, l;
  priors pr;
  
  //set the phenotype that we are analysing
  int phenfocus = -1;
  for (i = 0; i < phen->Nphenos; i++){
    if (phen->phenonames[i] == opts->phenoname){
      phenfocus = i;
    }
  }
  if (phenfocus == -1){
    cout << "Error: Phenotype " << opts->phenoname << " not found in phenotype file." << endl;
    throw(2);
  }
  
  //set parameters of the model from opts object
  int Npred = 1;
  if (opts->covarfile != "") Npred += cov->Ncovars;
  if (opts->conditionfile != "") Npred += conditionsnps.size();
  int Npheno = phen->phenocounts[phenfocus] - 1;
  int Nind;
  int Nvars;
  if (opts->dosagefile == ""){
	Nind = bed->Ninds;
	Nvars = bed->Nvars;
  } else {
    Nind = dosage->Ninds;
    Nvars = dosage->Nvars;
  }

  //unsigned concurentThreadsSupported = std::thread::hardware_concurrency();
  //cout << "Detected " << concurentThreadsSupported << " cores. Using them all." << endl;
  int nthreads = opts->threads;
  // initialize the testing object
  MultinomThread * MTs = new MultinomThread[nthreads];
  for (i = 0; i < nthreads; i++) MTs[i].LoadInitial(Npred, Nind, Npheno, opts, bed, dosage, cov, phen, conditionsnps, phenfocus);
  MultinomThread MT; 
  MT.LoadInitial(Npred, Nind, Npheno, opts, bed, dosage, cov, phen, conditionsnps, phenfocus);
  
  // initialize the output streams
  ofstream outfile, selectfile, errfile, pvalfile, fullparfile, fullerrfile, sefile;
  
  outfile.open((opts->outprefix + ".assoc.multinom").c_str());
  if (opts->modelSelect) selectfile.open((opts->outprefix + ".select.multinom").c_str());
  if (opts->outputErrs) errfile.open((opts->outprefix + ".err.multinom").c_str());
  if (opts->PhenoPs) pvalfile.open((opts->outprefix + ".phenops.multinom").c_str());
  if (opts->outputFullMd) {
	  fullerrfile.open((opts->outprefix + ".fullmd.errs").c_str());
	  fullparfile.open((opts->outprefix + ".fullmd.pars").c_str());
  }
  if (opts->ses){
  	sefile.open((opts->outprefix + ".ses.multinom").c_str());
  }
  
  // write the headers (this goes on for a while)
  int focus, focustemp;
  if (opts->modelSelect){
    if (opts->dosagefile == "")
        selectfile << "CHROM\tPOS\tRSID\tA1\tA2\tNULL";
     else
        selectfile << "RSID";
    for ( focus = 1; focus < floor(pow(2.0,Npheno)); focus++){
     selectfile << "\tM";
     focustemp = focus;
     for (i = (Npheno - 1); i >= 0; i--){
       if (focustemp >= floor(pow(2.0,i))){
	    for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == i + 1) selectfile << "_" << it->first;
	    focustemp -= pow(2.0,i);
	   }
     }
    }
    selectfile << "\n";
  }

  if (opts->outputErrs){
      if (opts->dosagefile == "")
          errfile << "CHROM\tPOS\tRSID\tA1\tA2";
       else
          errfile << "RSID";
     for (i = 1; i < (Npheno+1); i++){
		 for (j = 1; j < (Npheno + 1); j++){
	   	  	for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == i) errfile << "\t" << it->first;
	   	  	for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == j) errfile << "_" << it->first;			
		}
	 }
	 errfile << endl;
 }

 if (opts->PhenoPs){
   if (opts->dosagefile == "")
       pvalfile << "CHROM\tPOS\tRSID\tA1\tA2";
    else
       pvalfile << "RSID";
   for (i = 1; i < (Npheno+1); i++){
   for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == i) pvalfile << "\tP_" << it->first;
  }
  pvalfile << endl;
 }

 if (opts->ses){
   if (opts->dosagefile == "")
       sefile << "CHROM\tPOS\tRSID\tA1\tA2";
    else
       sefile << "RSID";
   for (i = 1; i < (Npheno+1); i++){
   for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == i) sefile << "\tSE_" << it->first << "\tZ_" << it->first << "\tP_" << it->first;
  }
  sefile << endl;
 }


 if (opts->outputFullMd){
   if (opts->dosagefile == ""){
       fullerrfile << "CHROM\tPOS\tRSID\tA1\tA2";
	   fullparfile << "CHROM\tPOS\tRSID\tA1\tA2";
   } else {
       fullerrfile << "RSID";
	   fullparfile << "RSID";
   }

   int nameCount = 0;

   vector<string> fullnames;
   string keepName;
   for (i = 1; i < (Npheno + 1); i++){
	 for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == i) keepName = it->first;
	 //cout << "A" << endl;
	 fullnames.push_back(keepName + "_intercept");
	 //cout << "B" << endl;
	 fullnames.push_back(keepName + "_geno");
	 //cout << "C" << endl;
	 for (j = 0; j < (Npred - 1); j++) {
		 fullnames.push_back(keepName + "_cov" + SSTR(j+1));
	 }  
   }

   for (i = 0; i < Npheno*(Npred+1); i++){
	   fullparfile << "\t" << fullnames[i];
	   for (j = 0; j < Npheno*(Npred + 1); j++){
		   fullerrfile << "\t" << fullnames[i] << "_" << fullnames[j];
	   }
  }
  fullparfile << endl;
  fullerrfile << endl;
 }

 if (opts->dosagefile == "")
    outfile << "CHROM\tPOS\tRSID\tA1\tA2";
  else
    outfile << "RSID";

  for (i = 1; i < (Npheno+1); i++){
    for (map<string,int>::iterator it=phen->phenomaps[phenfocus].begin(); it!=phen->phenomaps[phenfocus].end(); ++it) if (it->second == i) outfile << "\tOR_" << it->first;
  }
  if (opts->priorfile == "" && !opts->defaultPrior){
    outfile << "\tL0\tL1\tPVAL" << endl;
  } else {
    outfile << "\tL+P0\tL+P1\tlogBF" << endl;
  }
  //end header writing

  cout << "Carrying out association testing on " << Nvars << " SNPs." << endl;

  int snp;
  int found;
  double Lnull, Lalt;
  pthread_t * threadids = new pthread_t[nthreads];
  void *status;

  int threadcount = 0;
  snp = 0;
  while (threadcount < nthreads){
      found=0;
      for (i = 0; i < conditionsnps.size(); i++) if (conditionsnps[i] == snp) found=1;
      if (found) {snp++; continue;}
	  MTs[threadcount].SetSNP(snp);
	  pthread_create(&threadids[threadcount], NULL, runMT, (void *) &MTs[threadcount]);
	  snp++;
	  threadcount++;
	  if (snp >= Nvars) break; //escape if there are more SNPs than threads
  }
  
  int thread = 0;
  while (1){

	  // wait for the oldest thread to finish
	  pthread_join(threadids[thread], &status);
	 // cout << snp << " " << thread << " " << MTs[thread].snp << " " << Nvars << endl;
 	  //write all the outputs for that thread
	  if (!opts->nooutput){
		  outfile << MTs[thread].assocOutput;
		  if (opts->modelSelect) selectfile << MTs[thread].selectOutput;
		  if (opts->ses) sefile << MTs[thread].waldOutput;
		  if (opts->outputErrs) errfile << MTs[thread].errorOutput;
		  if (opts->outputFullMd) {fullerrfile << MTs[thread].modelErrOutput; fullparfile << MTs[thread].modelParOutput;}
		  if (opts->PhenoPs) pvalfile << MTs[thread].phenoLTRsOutput;
	  }
	  
	  //if we have printed all SNPs, break
	  if (MTs[thread].snp >= (Nvars-1)) break;
	  
	  //otherwise, if we have SNPs left to do, find the next one and set it off
	  found = 0;
	  while (found != 0){
      	for (i = 0; i < conditionsnps.size(); i++) if (conditionsnps[i] == snp) found=1;
	   	if (found == 1) snp++;
	  }
	  
	  if (snp < Nvars){
   	 	 MTs[thread].SetSNP(snp);
   	  	 pthread_create(&threadids[thread], NULL, runMT, (void *) &MTs[thread]);
	  }
	  snp++;
	  thread = (1 + thread) % nthreads;
  }
  
  /*
  // fit the model for each SNP
  for (snp = 0; snp < Nvars; snp++){
     
	  // skip this SNP if it is being conditioned on
     found=0;
     for (i = 0; i < conditionsnps.size(); i++) if (conditionsnps[i] == snp) found=1;
     if (found) continue;	
		 
	 // set the SNP
	 MTs[0].SetSNP(snp);
	 
	 //run all the tests
	 //MT.RunTests();
	 pthread_create(&threadid, NULL, runMT, (void *) &MTs[0]);
	 pthread_join(threadid, &status);
	 
	 //write all the outputs
	 outfile << MTs[0].assocOutput;
     if (opts->modelSelect) selectfile << MTs[0].selectOutput;
	 if (opts->ses) sefile << MTs[0].waldOutput;
	 if (opts->outputErrs) errfile << MTs[0].errorOutput;
	 if (opts->outputFullMd) {fullerrfile << MTs[0].modelErrOutput; fullparfile << MTs[0].modelParOutput;}
	 if (opts->PhenoPs) pvalfile << MTs[0].phenoLTRsOutput;
 }
 //end per-variant model fitting
   */
  return(0);
}


int main (int argn, char **argv){
	// run trinculo!

	// detect the mode (ordinal or multinomial)
  int mode = 0;
  if (argn == 1) {
	cout << "No options entered! Use trinculo multinom --help for usage." << endl;
	return(1);
  }
  
  if (strcmp(argv[1],"multinom") == 0){
    cout << "Running a multinomial logistic analysis" << endl;
    mode = 1;
  }  else if (strcmp(argv[1],"ordinal") == 0){
    cout << "Running a ordinal (proportional odds) logistic analysis" << endl;
    mode = 2;
  } else {
    cout << "Error: mode " <<  argv[1] << " not recognised. Use trinculo multinom --help for usage." << endl;
    return(1);
  }
  argv++; argn--;    
  //end mode selection

  //initialize some data objects
  bedfile bed; //binary plink data
  dosagefile dosage; //dosage data
  covars cov; //confounder data
  phenos phen; //phenotype data
  vector<string> SNParray; //vector of SNP ids
  vector<int> conditionsnps; //vector of SNPs to condition on (specifies entries in SNParray)

  //parse the command line arguments and store them in the opts object
  inputs opts;
  try { 
  opts.parseinput (argn,argv);
  } 
  catch (int e)
  { 
    if (e == 1) return(0);
    cout << "Error reading inputs (" << e << ")." << endl;
    return(1);
  }
  
  //make a log file
  ofstream logfile;
  logfile.open((opts.outprefix + ".log").c_str());
  logfile << "Start time: " << currentDateTime() << endl;
  logfile << "Running trinculo v0.96" << endl;
  
  map<string,int>::iterator it;
  
  //read in genotype data, either from dosage or from binary plink data
  if (opts.dosagefile != ""){
	  logfile << "Reading dosage-format data from " << opts.dosagefile << endl;
    dosage.readDosage(opts.dosagefile);
    dosage.makeIDMap();
    SNParray = dosage.RSID;
  } else if (opts.bedprefix != "") {
	  logfile << "Reading plink-format data from " << opts.bedprefix << ".*" << endl;
    bed.readBed(opts.bedprefix);
    bed.makeIDMap();
    SNParray = bed.RSID;
  } else {
    cout << "Error: No genotypes provided!" << endl;
    return(1);
  }

  //read in SNPs to condition on from a text file, and look for them in the genotype objects
  if (opts.conditionfile != ""){
    string snp;
    ifstream file;
    int i;
    int found;
    file.open(opts.conditionfile.c_str());
    while (!file.eof()){
      getline(file,snp);
      if (snp == "") break;
      //cout << snp << endl;
      found = 0;
      for (i = 0; i < SNParray.size(); i++)
	if (!SNParray[i].compare(snp)){
	  conditionsnps.push_back(i);
	  //cout << i << endl;
	  found=1;
	  break;
	}
      if (found == 0)
	cout << "Conditioning SNP " << snp << " not found in genotype data. Ignoring entry." << endl;
    }
    
    cout << "Conditioning on " << conditionsnps.size() << " SNPs." << endl;
    logfile << "Conditioning on " << conditionsnps.size() << " SNPs from " <<  opts.conditionfile << endl;
	
   }
  
   // read in confounders (these are automatically matched up to the genotype data) 
  if (opts.covarfile != "") {
	  
    if (opts.dosagefile == "") {
      cov.readCovar(opts.covarfile.c_str(),bed.IDMap,bed.Ninds,opts.normalizecovar);
    } else {
      cov.readCovar(opts.covarfile.c_str(),dosage.IDMap,dosage.Ninds,opts.normalizecovar);
    }
    logfile << "Including " << cov.Ncovars << " covariates from " << opts.covarfile << endl;
	
  }

   //read in phenotypes (these are automatically matched up to the genotype data) 
  if (mode == 1)
    if (opts.dosagefile == "")
      phen.readPhenos(opts.phenofile.c_str(),bed.IDMap,bed.Ninds,opts.basepheno,opts.missingpheno,0); 
    else
      phen.readPhenos(opts.phenofile.c_str(),dosage.IDMap,dosage.Ninds,opts.basepheno,opts.missingpheno,0); 
  else
    if (opts.dosagefile == "") 
      phen.readPhenos(opts.phenofile.c_str(),bed.IDMap,bed.Ninds,opts.basepheno,opts.missingpheno,1);
    else
      phen.readPhenos(opts.phenofile.c_str(),dosage.IDMap,dosage.Ninds,opts.basepheno,opts.missingpheno,1);
  
  logfile << "Reading " << phen.Nphenos << " phenotypes from " << opts.phenofile << endl;

  //starting multinomial mode, if requested
  if (mode == 1){
	  
	  // calculate an empirical covariance prior and write it to a file, if requested
	  if (opts.empiricalCovar) {
		  logfile << "Calculating empiricial prior and writing to " << opts.outprefix << ".prior" << endl;
		  
		  ofstream outfile;
		  outfile.open((opts.outprefix + ".prior").c_str());
		  opts.priorfile = (opts.outprefix + ".prior").c_str();
		  int i,j;
		  double *answer;
		  EstCovMat betaData;
		  betaData = inferCovar(&opts, &bed, &dosage, &cov, &phen, conditionsnps);
		  answer = betaData.InferCovariance();
		  int Npheno = betaData.k;
		  cout << "Inferred phenotype covariance matrix:" << endl;
		  for (i = 0; i < Npheno; i++) {

			  for (j = 0; j < Npheno; j++){
				if (i <= j){
				 	cout << " " << answer[j + i*Npheno - i*(i+1)/2];
				  	outfile << " " << answer[j + i*Npheno - i*(i+1)/2];
				} else {
				 	cout << " " << answer[i + j*Npheno - j*(j+1)/2];
				  	outfile << " " << answer[i + j*Npheno - j*(j+1)/2];
				}
			  }
			  cout << endl;
			  outfile << endl;
		  }
	
	  }
	  
	  //carry out per-variant model fitting, unless requested not to
	  if (!opts.noAssoc) {
		  logfile << "Running multinomial analysis and writing results to " << opts.outprefix << ".assoc.multinom" << endl; 
		  if (!runMultinom(&opts, &bed, &dosage, &cov, &phen, conditionsnps)) cout << "Analyses finished." << endl;
	
	  }
  }
  
  //starting ordinal mode, if requested
  if (mode == 2){
	  logfile << "Running ordinal analysis and writing results to " << opts.outprefix << ".assoc.ordinal" << endl; 
	  if (!runPropOdds(&opts, &bed, &dosage, &cov, &phen,conditionsnps)) cout << "Analyses finished." << endl;
  }
  
  logfile << "End time: " << currentDateTime() << endl;
  
  return(0);
}
