/*  _______________________________________________________________________

    DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
    Copyright 2014 Sandia Corporation.
    This software is distributed under the GNU Lesser General Public License.
    For more information, see the README file in the top Dakota directory.
    _______________________________________________________________________ */

//- Class:	     SensAnalysisGlobal
//- Description: Utility helper class which has correlations and VBD
//- Owner:       Laura Swiler, Brian Adams, Ahmad Rushdi
//- Checked by:
//- Version:

#include "SensAnalysisGlobal.hpp"
#include "ResultsManager.hpp"
#include "dakota_linear_algebra.hpp"
#include <algorithm>
#include <boost/iterator/counting_iterator.hpp>

static const char rcsId[]="@(#) $Id: SensAnalysisGlobal.cpp 6170 2009-10-06 22:42:15Z lpswile $";

namespace Dakota {

RealArray SensAnalysisGlobal::rawData = RealArray();

bool SensAnalysisGlobal::rank_sort(const int& x, const int& y)
{ return rawData[x]<rawData[y]; }

size_t SensAnalysisGlobal::
find_valid_samples(const IntResponseMap& resp_samples, BoolDeque& valid_sample)
{
  // TODO: later compute correlation on per-response basis to keep
  // partial faults
  using std::isfinite;

  size_t num_obs = resp_samples.size(), num_valid_samples = 0;
  IntRespMCIter it = resp_samples.begin();
  for (size_t j=0; j<num_obs; ++j, ++it) {
    valid_sample[j] = true;
    for (size_t k=0; k<numFns; ++k)
      if (!isfinite(it->second.function_value(k))) {
        valid_sample[j] = false;
        break;
      }
    if (valid_sample[j])
      ++num_valid_samples;
  }

  return num_valid_samples;
}

void SensAnalysisGlobal::
valid_sample_matrix(const VariablesArray& vars_samples,
                    const IntResponseMap& resp_samples,
                    const StringSetArray& dss_vals,
                    const BoolDeque is_valid_sample,
                    RealMatrix& valid_data)
{
  int num_obs = vars_samples.size(), num_corr = valid_data.numRows();
  IntRespMCIter it = resp_samples.begin();
  for (size_t j=0, s_cntr=0; j<num_obs; ++j, ++it)
    if (is_valid_sample[j]) {
      // get a view of the first numVars rows of the samples col
      RealVector td_col_vars(Teuchos::View, valid_data[s_cntr], (int)numVars);
      vars_samples[j].as_vector(dss_vals, td_col_vars);
      // get a view of the last numFns rows of the samples col
      RealVector td_col_resp(Teuchos::View, valid_data[s_cntr] + numVars,
                             (int)numFns);
      copy_data(it->second.function_values(), td_col_resp);
      ++s_cntr;
    }
}

void SensAnalysisGlobal::
valid_sample_matrix(const RealMatrix&     vars_samples,
                    const IntResponseMap& resp_samples,
                    const BoolDeque is_valid_sample,
                    RealMatrix& valid_data)
{
  int num_obs = vars_samples.numCols(), num_corr = valid_data.numRows();
  IntRespMCIter it = resp_samples.begin();
  for (int j=0, s_cntr=0; j<num_obs; ++j, ++it)
    if (is_valid_sample[j]) {
      for (int i=0; i<numVars; ++i)
        valid_data(i, s_cntr) = vars_samples(i, j);
      // get a view of the last numFns rows of the samples col
      RealVector td_col_resp(Teuchos::View, valid_data[s_cntr] + numVars,
                             (int)numFns);
      copy_data(it->second.function_values(), td_col_resp);
      ++s_cntr;
    }
}

/** When converting values to ranks, uses the average ranks of any tied values */
void SensAnalysisGlobal::values_to_ranks(RealMatrix& valid_data)
{
  int num_corr = valid_data.numRows(), num_valid_samples = valid_data.numCols();
  // for each var/resp
  for (int i=0; i<num_corr; ++i) {
    // create a multimap from value to array index (so it is sorted by value and
    // the ranks are given by the map order); don't need a stable sort as we are
    // replacing the tied values by their average rank
    RealIntMultiMap vals_inds;
    for (int j=0; j<num_valid_samples; ++j)
      vals_inds.insert(std::make_pair(valid_data(i,j), j));

    // iterate for each unique value and find tied values
    RealIntMultiMap::const_iterator vi_it = vals_inds.begin();
    RealIntMultiMap::const_iterator vi_end = vals_inds.end();
    for (int rank=0; vi_it != vi_end; ) {
      // find a range of tied values
      double value = vi_it->first;
      std::pair<RealIntMultiMap::const_iterator, RealIntMultiMap::const_iterator>
	tied_range = vals_inds.equal_range(value);
      int num_ties = std::distance(tied_range.first, tied_range.second);
      double avg_rank = (rank + rank+num_ties-1) / 2.0;
      // all tied values get assigned the average rank
      for ( ; tied_range.first != tied_range.second; ++tied_range.first)
	valid_data(i, tied_range.first->second) = avg_rank;
      // increment to the next unequal value
      vi_it = tied_range.second;
      rank += num_ties;
    }
  }
}

void SensAnalysisGlobal::center_rows(RealMatrix& data_matrix)
{
  int num_row = data_matrix.numRows(), num_col = data_matrix.numCols();
  for (int i=0; i<num_row; i++) {
    // normalize each row (input/output factor) by its mean across observations
    Real row_mean = 0.0;
    for (int j=0; j<num_col; j++)
      row_mean += data_matrix(i,j);
    row_mean /= (Real)num_col;
    for (int j=0; j<num_col; j++)
      data_matrix(i,j) -= row_mean;
  }
}

void SensAnalysisGlobal::correl_adjust(Real& corr_value)
{
  if (std::isfinite(corr_value) && std::abs(corr_value) > 1.0)
    corr_value = corr_value / std::abs(corr_value);
}

/** This version is used when full variables objects are being
    processed. Calculates simple correlation, partial correlation,
    simple rank correlation, and partial rank correlation
    coefficients. */
void SensAnalysisGlobal::
compute_correlations(const VariablesArray& vars_samples,
                     const IntResponseMap& resp_samples,
                     const StringSetArray& dss_vals)
{
  size_t num_obs = vars_samples.size();
  if (num_obs == 0) {
    Cerr << "Error: Number of samples must be nonzero in SensAnalysisGlobal::"
         << "compute_correlations()." << std::endl;
    abort_handler(-1);
  }
  if (resp_samples.size() != num_obs) {
    Cerr << "Error: Mismatch in array lengths in SensAnalysisGlobal::"
         << "compute_correlations()." << std::endl;
    abort_handler(-1);
  }

  numVars = vars_samples[0].cv() + vars_samples[0].div() +
    vars_samples[0].dsv() + vars_samples[0].drv();
  numFns  = resp_samples.begin()->second.num_functions();
  int num_corr = numVars + numFns;

  // determine which samples have valid responses
  BoolDeque is_valid_sample(num_obs);
  int num_valid_samples = find_valid_samples(resp_samples, is_valid_sample);

  // The following calls regenerate and destroy the valid_data matrix
  // to save memory

  // create a matrix containing only the valid sample data
  RealMatrix valid_data(num_corr, num_valid_samples);

  // calculate simple rank correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, dss_vals, is_valid_sample,
                      valid_data);
  simple_corr(valid_data, num_corr, simpleCorr);

  // calculate partial correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, dss_vals, is_valid_sample,
                      valid_data);
  partial_corr(valid_data, numVars, simpleCorr, partialCorr, numericalIssuesRaw);

  // calculate simple rank correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, dss_vals, is_valid_sample,
                      valid_data);
  values_to_ranks(valid_data);
  simple_corr(valid_data, num_corr, simpleRankCorr);

  // calculate partial rank correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, dss_vals, is_valid_sample,
                      valid_data);
  values_to_ranks(valid_data);
  partial_corr(valid_data, numVars, simpleRankCorr, partialRankCorr,
               numericalIssuesRank);

  corrComputed = true;
}

/** This version is used when compact samples matrix is being
    processed.  Calculates simple correlation, partial correlation,
    simple rank correlation, and partial rank correlation
    coefficients. */
void SensAnalysisGlobal::
compute_correlations(const RealMatrix&     vars_samples,
                     const IntResponseMap& resp_samples)
{
  int num_obs = vars_samples.numCols();
  if (!num_obs) {
    Cerr << "Error: Number of samples must be nonzero in SensAnalysisGlobal::"
         << "compute_correlations()." << std::endl;
    abort_handler(-1);
  }
  if (resp_samples.size() != num_obs) {
    Cerr << "Error: Mismatch in array lengths in SensAnalysisGlobal::"
         << "compute_correlations()." << std::endl;
    abort_handler(-1);
  }

  numVars = vars_samples.numRows();
  numFns  = resp_samples.begin()->second.num_functions();
  int num_corr = numVars + numFns;

  // determine which samples have valid responses
  BoolDeque is_valid_sample(num_obs);
  int num_valid_samples = find_valid_samples(resp_samples, is_valid_sample);

  // The following calls regenerate and destroy the valid_data matrix
  // to save memory

  // create a matrix containing only the valid sample data
  RealMatrix valid_data(num_corr, num_valid_samples);

  // calculate simple rank correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, is_valid_sample, valid_data);
  simple_corr(valid_data, num_corr, simpleCorr);

  // calculate partial correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, is_valid_sample, valid_data);
  partial_corr(valid_data, numVars, simpleCorr, partialCorr, numericalIssuesRaw);

  // calculate simple rank correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, is_valid_sample, valid_data);
  values_to_ranks(valid_data);
  simple_corr(valid_data, num_corr, simpleRankCorr);

  // calculate partial rank correlation coeff
  valid_sample_matrix(vars_samples, resp_samples, is_valid_sample, valid_data);
  values_to_ranks(valid_data);
  partial_corr(valid_data, numVars, simpleRankCorr, partialRankCorr,
               numericalIssuesRank);

  corrComputed = true;
}

/** Calculates simple correlation coefficients from a matrix of data
    (oriented factors x observations):
     - num_corr is number of rows of total data
     - num_in indicates whether only pairs of correlations should be
       calculated between pairs of columns (num_in
       vs. num_corr-num_in); if num_in = num_corr, correlations are
       calculated between all columns */
void SensAnalysisGlobal::
simple_corr(RealMatrix& total_data, const int& num_in, RealMatrix& corr_matrix)
{
  int num_corr = total_data.numRows(), num_obs = total_data.numCols();

  center_rows(total_data);

  for (int i=0; i<num_corr; i++) {
    // calculate sum of squares for each factor (row)
    Real row_sumsq = 0.0;
    for (int j=0; j<num_obs; j++)
      row_sumsq += total_data(i,j)*total_data(i,j);
    row_sumsq = std::sqrt(row_sumsq);
    // normalize the rows with the sumsquare term
    for (int j=0; j<num_obs; j++)
      total_data(i,j) /= row_sumsq;
  }

  // calculate matrix of simple correlation coefficients
  if (num_corr == num_in) {
    // all-to-all case
    corr_matrix.shape(num_corr, num_corr);
    if (num_obs <= 1)
      corr_matrix.putScalar(std::numeric_limits<double>::quiet_NaN());
    else {
      corr_matrix.multiply(Teuchos::NO_TRANS, Teuchos::TRANS, 1.0,
			   total_data, total_data, 0.0);
      for (int i=0; i<num_corr; ++i) {
	// set finite diagonal values to 1.0
	if (std::isfinite(corr_matrix(i,i)))
	  corr_matrix(i,i) = 1.0;
	// snap all finite values to [-1.0, 1.0]
	for (int j=0; j<i; ++j) {
	  correl_adjust(corr_matrix(i,j));
	  correl_adjust(corr_matrix(j,i));
	}
      }
    }
  }
  else {
    // input-to-output case
    int num_out = num_corr - num_in;
    corr_matrix.shape(num_in, num_out);
    if (num_obs <= 1)
      corr_matrix.putScalar(std::numeric_limits<double>::quiet_NaN());
    else {
      RealMatrix total_data_in(Teuchos::View, total_data, num_in, num_obs, 0, 0);
      RealMatrix total_data_out(Teuchos::View, total_data, num_out, num_obs,
				num_in, 0);
      corr_matrix.multiply(Teuchos::NO_TRANS, Teuchos::TRANS, 1.0,
			   total_data_in, total_data_out, 0.0);
      // snap all finite values to [-1.0, 1.0]
      for (int i=0; i<num_in; ++i)
	for (int j=0; j<num_out; ++j)
	  correl_adjust(corr_matrix(i,j));
    }
  }
}

/** Calculates partial correlation coefficients between num_in inputs
    and numRows() - num_in outputs. */
void SensAnalysisGlobal::
partial_corr(RealMatrix& total_data, const int num_in,
             const RealMatrix& simple_corr_mat,
             RealMatrix& corr_matrix, bool& numerical_issues)
{
  int num_obs = total_data.numCols(), num_out = total_data.numRows() - num_in;

  // initialize output data
  corr_matrix.reshape(num_in, num_out);
  numerical_issues = false;
  // TODO: return numerical issues per-input
  BoolDeque numerical_except(num_in, false);

  if (num_obs <= 1) {
    corr_matrix.putScalar(std::numeric_limits<double>::quiet_NaN());
    numerical_issues = true;
    return;
  }

  // For a single input factor, partial = simple (no controlling factors)
  if (num_in == 1) {
    for (int k=0; k<num_out; ++k)
      corr_matrix(0, k) = simple_corr_mat(0, k+1);
    return;
  }

  center_rows(total_data);

  // matrix of X = [Vi | R ]; the response cols don't change per variable
  RealMatrix correl_factors_X(num_obs, 1 + num_out);
  for (int j=0; j<num_obs; ++j)
    for (int k=0; k<num_out; ++k)
      correl_factors_X(j, 1+k) = total_data(num_in+k, j);
  // matrix of Z = [V~i]
  RealMatrix control_factors_Z(num_obs, num_in - 1);

  for (int i=0; i<num_in; ++i) {

    // partial correlation analysis for Vi, R, controlling for
    // V~i. Transpose everything to line up with more typical
    // convention Nobs x (Nvar + Nresp)
    for (int j=0; j<num_obs; ++j) {
      correl_factors_X(j, 0) = total_data(i, j); // Vi
      // (response columns are already populated above)
      // V~i
      for (int k=0; k<i; ++k)
        control_factors_Z(j, k) = total_data(k, j);
      for (int k=i+1; k<num_in; ++k)
        control_factors_Z(j, k-1) = total_data(k, j);
    }

    // form partial_cov = X'X - (X'Z)*inv(Z'Z)*(Z'X), preserving
    // symmetric and positive definite
    int mult_err = 0;

    // intialize to X'X
    RealMatrix partial_cov(1 + num_out, 1 + num_out);
    mult_err |= partial_cov.multiply(Teuchos::TRANS, Teuchos::NO_TRANS, 1.0,
				     correl_factors_X, correl_factors_X, 0.0);

    // Here we use truncated SVD to account for the case where there
    // are fewer data points than variables, including the degenerate
    // case of 2 observations.  Should be equivalent to minimum-norm
    // least squares in the regressions.

    RealMatrix Zinv_Zt_X;  // a bit misnamed, since Z is rectangular
    bool use_qr = false;
    if (use_qr) {
      Zinv_Zt_X.reshape(num_in - 1, 1 + num_out);
      // initialize to Z'X
      mult_err |= Zinv_Zt_X.multiply(Teuchos::TRANS, Teuchos::NO_TRANS, 1.0,
                                     control_factors_Z, correl_factors_X, 0.0);

      // factor Z = QR; in-place QR factorization destroys Z (this never errors)
      qr(control_factors_Z);

      // now backsolve to update to Rinv' * Z'X (a failed backsolve leaves junk)
      int qrs_info = qr_rsolve(control_factors_Z, true, Zinv_Zt_X);
      numerical_except[i] = numerical_except[i] || (qrs_info != 0);
    }
    else {
      // alternative with svd to numerical precision; some concern
      // remains that this might not yield nan/inf when it should

      // initialize to Z'X
      RealMatrix Zt_X(num_in - 1, 1 + num_out);
      mult_err |= Zt_X.multiply(Teuchos::TRANS, Teuchos::NO_TRANS, 1.0,
                                control_factors_Z, correl_factors_X, 0.0);
      RealVector sing_vals;
      RealMatrix v_trans;
      svd(control_factors_Z, sing_vals, v_trans);

      double tol =
        std::numeric_limits<double>::epsilon() * control_factors_Z.normInf();
      int sv_keep = 0;
      for ( ; sv_keep < sing_vals.length(); ++sv_keep)
        if (sing_vals[sv_keep] < tol)
          break;
      // TODO: Could opt for stricter check here:
      // if (sv_keep < std::min(num_obs, num_in-1))
      if (sv_keep == 0)
        numerical_except[i] = true;
      else {
        v_trans.reshape(sv_keep, num_in - 1);
        Zinv_Zt_X.reshape(sv_keep, 1 + num_out);
        Zinv_Zt_X.multiply(Teuchos::NO_TRANS, Teuchos::NO_TRANS, 1.0,
                           v_trans, Zt_X, 0.0);
        for (int j=0; j<sv_keep; ++j)
          for (int k=0; k < (1 + num_out); ++k)
            Zinv_Zt_X(j,k) /= sing_vals[j];
      }
    }

    if (!numerical_except[i])
      // X'X - (X'Z)*inv(Z'Z)*(Z'X) = X'X - (Zinv*Z'X)'(Zinv*Z'X)
      mult_err |= partial_cov.multiply(Teuchos::TRANS, Teuchos::NO_TRANS, -1.0,
				       Zinv_Zt_X, Zinv_Zt_X, 1.0);

    // This should never happen:
    if (mult_err != 0) {
      Cerr << "\nError (partial_corr): multiplying incompatible matrices.\n";
      abort_handler(-1);
    }
    numerical_issues = numerical_issues || numerical_except[i];

    for (int k=0; k<num_out; ++k)
      if (numerical_except[i])
        corr_matrix(i,k) = std::numeric_limits<Real>::quiet_NaN();
      else
        corr_matrix(i,k) = partial_cov(0, k+1) / std::sqrt(partial_cov(0,0)) /
          std::sqrt(partial_cov(k+1, k+1));
  }

  // snap all finite values to [-1.0, 1.0]
  for (int i=0; i<num_in; ++i)
    for (int j=0; j<num_out; ++j)
      correl_adjust(corr_matrix(i,j));
}

// Return true if any correlation coefficient is NaN or Inf, false otherwise
bool SensAnalysisGlobal::has_nan_or_inf(const RealMatrix &corr) const {
  int num_rows = corr.numRows(), num_cols = corr.numCols();
  for(int j = 0; j < num_cols; ++j)
    for(int i = 0; i < num_rows; ++i)
      if( ! std::isfinite(corr(i,j)))
        return true;
  return false;
}

// TODO: combine archive with print
void SensAnalysisGlobal::
archive_correlations(const StrStrSizet& run_identifier,
		     ResultsManager& iterator_results,
		     StringMultiArrayConstView cv_labels,
		     StringMultiArrayConstView div_labels,
		     StringMultiArrayConstView dsv_labels,
		     StringMultiArrayConstView drv_labels,
		     const StringArray& resp_labels) const
{
  if (!iterator_results.active())  return;

  int num_in_out = numVars + numFns;
  if (simpleCorr.numRows() == num_in_out &&
      simpleCorr.numCols() == num_in_out) {
    MetaDataType md;
    md["Row labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, resp_labels);
    md["Column labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, resp_labels);
    iterator_results.insert(run_identifier,
			    iterator_results.results_names.correl_simple_all,
			    simpleCorr, md);
  }
  else if (simpleCorr.numRows() == numVars &&
	   simpleCorr.numCols() == numFns) {
    MetaDataType md;
    md["Row labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, StringArray());
    md["Column labels"] = make_metadatavalue(resp_labels);
    iterator_results.insert(run_identifier,
			    iterator_results.results_names.correl_simple_io,
			    simpleCorr, md);
  }

  if (partialCorr.numRows() == numVars &&
      partialCorr.numCols() == numFns) {
    MetaDataType md;
    md["Row labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, StringArray());
    md["Column labels"] = make_metadatavalue(resp_labels);
    iterator_results.insert(run_identifier,
			    iterator_results.results_names.correl_partial_io,
			    partialCorr, md);
  }
  // TODO: metadata
  //  if (numericalIssuesRaw)

  if (simpleRankCorr.numRows() == num_in_out &&
      simpleRankCorr.numCols() == num_in_out) {
    MetaDataType md;
    md["Row labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, resp_labels);
    md["Column labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, resp_labels);
    iterator_results.insert(run_identifier,
			    iterator_results.results_names.correl_simple_rank_all,
			    simpleRankCorr, md);
  }
  else if (simpleRankCorr.numRows() == numVars &&
	   simpleRankCorr.numCols() == numFns) {
    MetaDataType md;
    md["Row labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, StringArray());
    md["Column labels"] = make_metadatavalue(resp_labels);
    iterator_results.insert(run_identifier,
			    iterator_results.results_names.correl_simple_rank_io,
			    simpleRankCorr, md);

  }

  if (partialRankCorr.numRows() == numVars &&
      partialRankCorr.numCols() == numFns) {
    MetaDataType md;
    md["Row labels"] =
      make_metadatavalue(cv_labels, div_labels, dsv_labels, drv_labels, StringArray());
    md["Column labels"] = make_metadatavalue(resp_labels);
    iterator_results.insert(run_identifier,
			    iterator_results.results_names.correl_partial_rank_io,
			    partialRankCorr, md);
  }
}

void SensAnalysisGlobal::
print_correlations(std::ostream& s, StringMultiArrayConstView cv_labels,
		   StringMultiArrayConstView div_labels,
		   StringMultiArrayConstView dsv_labels,
		   StringMultiArrayConstView drv_labels,
		   const StringArray& resp_labels) const
{
  // output correlation matrices

  if (!corrComputed) {
    Cout << "Correlation matrices not computed." << std::endl;
    return;
  }

  if( has_nan_or_inf(simpleCorr) ||
      has_nan_or_inf(partialCorr) ||
      has_nan_or_inf(simpleRankCorr) ||
      has_nan_or_inf(partialRankCorr) )
    s << "\n\nAt least one correlation coefficient is nan or inf. This " <<
      "commonly occurs when\ndiscrete variables (including histogram " <<
      "variables) are present, a response is\ncompletely insensitive to " <<
      "variables (response variance equal to 0), there are\nfewer samples " <<
      "than variables, or some samples are approximately collinear." <<
      std::endl;

  s << std::scientific << std::setprecision(5);

  if (resp_labels.size() != numFns) {
    Cerr << "Error: Number of response labels (" << resp_labels.size()
	 << ") passed to print_correlations not equal to number of output "
	 << "functions (" << numFns << ") in compute_correlations()."
	 << std::endl;
    abort_handler(-1);
  }

  size_t i, j, num_cv = cv_labels.size(), num_div = div_labels.size(),
    num_dsv = dsv_labels.size(), num_drv = drv_labels.size();
  if (num_cv+num_div+num_dsv+num_drv != numVars) {
    Cerr << "Error: Number of variable labels (" << num_cv+num_div+num_drv
	 << ") passed to print_correlations not equal to number of input "
	 << "variables (" << numVars << ") in compute_correlations()." << std::endl;
    abort_handler(-1);
  }

  int num_in_out = numVars + numFns;
  if (simpleCorr.numRows() == num_in_out &&
      simpleCorr.numCols() == num_in_out) {
    s << "\nSimple Correlation Matrix among all inputs and outputs:\n"
      << "             ";
    for (i=0; i<num_cv; ++i)
      s << std::setw(12) << cv_labels[i] << ' ';
    for (i=0; i<num_div; ++i)
      s << std::setw(12) << div_labels[i] << ' ';
    for (i=0; i<num_dsv; ++i)
      s << std::setw(12) << dsv_labels[i] << ' ';
    for (i=0; i<num_drv; ++i)
      s << std::setw(12) << drv_labels[i] << ' ';
    for (i=0; i<numFns; ++i)
      s << std::setw(12) << resp_labels[i] << ' ';
    s << '\n';
    for (i=0; i<num_in_out; ++i) {
      if (i<num_cv)
	s << std::setw(12) << cv_labels[i] << ' ';
      else if (i<num_cv+num_div)
	s << std::setw(12) << div_labels[i-num_cv] << ' ';
      else if (i<num_cv+num_div+num_dsv)
	s << std::setw(12) << dsv_labels[i-num_cv-num_div] << ' ';
      else if (i<numVars)
	s << std::setw(12) << drv_labels[i-num_cv-num_div-num_dsv] << ' ';
      else
	s << std::setw(12) << resp_labels[i-numVars] << ' ';
      for (j=0; j<=i; ++j)
	s << std::setw(12) << simpleCorr(i,j) << ' ';
      s << '\n';
    }
  }
  else if (simpleCorr.numRows() == numVars &&
	   simpleCorr.numCols() == numFns) {
    s << "\nSimple Correlation Matrix between input and output:\n"
      << "             ";
    for (j=0; j<numFns; ++j)
      s << std::setw(12) << resp_labels[j] << ' ';
    s << '\n';
    for (i=0; i<numVars; ++i) {
      if (i<num_cv)
	s << std::setw(12) << cv_labels[i] << ' ';
      else if (i<num_cv+num_div)
	s << std::setw(12) << div_labels[i-num_cv] << ' ';
      else if (i<num_cv+num_div+num_dsv)
	s << std::setw(12) << dsv_labels[i-num_cv-num_div] << ' ';
      else
	s << std::setw(12) << drv_labels[i-num_cv-num_div-num_dsv] << ' ';
      for (j=0; j<numFns; ++j)
	s << std::setw(12) << simpleCorr(i,j) << ' ';
      s << '\n';
    }
  }

  if (partialCorr.numRows() == numVars &&
      partialCorr.numCols() == numFns) {
    s << "\nPartial Correlation Matrix between input and output:\n"
      << "             ";
    for (j=0; j<numFns; ++j)
      s << std::setw(12) << resp_labels[j]<<' ';
    s << '\n';
    for (i=0; i<numVars; ++i) {
      if (i<num_cv)
	s << std::setw(12) << cv_labels[i] << ' ';
      else if (i<num_cv+num_div)
	s << std::setw(12) << div_labels[i-num_cv] << ' ';
      else if (i<num_cv+num_div+num_dsv)
	s << std::setw(12) << dsv_labels[i-num_cv-num_div] << ' ';
      else
	s << std::setw(12) << drv_labels[i-num_cv-num_div-num_dsv] << ' ';
      for (j=0; j<numFns; ++j)
	s << std::setw(12) << partialCorr(i,j) << ' ';
      s << '\n';
    }
  }
  //  This warning message has been supplanted by more generic tests for NaNs and
  //  Infs
  //if (numericalIssuesRaw)
  //  s << "\nThere may be some numerical issues associated with the calculation "
  //    << "of the \npartial correlation coefficients above.  This can be due to "
  //    << "very small \nnumbers of input samples, or to ill-conditioned matrices"
  //    << ", \nin situations where the partials are very close to zero, -1, or "
  //    << "+1.\n";

  if (simpleRankCorr.numRows() == num_in_out &&
      simpleRankCorr.numCols() == num_in_out) {
    s << "\nSimple Rank Correlation Matrix among all inputs and outputs:\n"
      << "             ";
    for (i=0; i<num_cv; ++i)
      s << std::setw(12) << cv_labels[i] << ' ';
    for (i=0; i<num_div; ++i)
      s << std::setw(12) << div_labels[i] << ' ';
    for (i=0; i<num_dsv; ++i)
      s << std::setw(12) << dsv_labels[i] << ' ';
    for (i=0; i<num_drv; ++i)
      s << std::setw(12) << drv_labels[i] << ' ';
    for (i=0; i<numFns; ++i)
      s << std::setw(12) << resp_labels[i] << ' ';
    s << '\n';
    for (i=0; i<num_in_out; ++i) {
      if (i<num_cv)
	s << std::setw(12) << cv_labels[i] << ' ';
      else if (i<num_cv+num_div)
	s << std::setw(12) << div_labels[i-num_cv] << ' ';
      else if (i<num_cv+num_div+num_dsv)
	s << std::setw(12) << dsv_labels[i-num_cv-num_div] << ' ';
      else if (i<numVars)
	s << std::setw(12) << drv_labels[i-num_cv-num_div-num_dsv] << ' ';
      else
	s << std::setw(12) << resp_labels[i-numVars] << ' ';
      for (j=0; j<=i; ++j)
	s << std::setw(12) << simpleRankCorr(i,j) << ' ';
      s << '\n';
    }
  }
  else if (simpleRankCorr.numRows() == numVars &&
	   simpleRankCorr.numCols() == numFns) {
    s << "\nSimple Rank Correlation Matrix between input and output:\n"
      << "             ";
    for (j=0; j<numFns; ++j)
      s << std::setw(12) << resp_labels[j] << ' ';
    s << '\n';
    for (i=0; i<numVars; ++i) {
      if (i<num_cv)
	s << std::setw(12) << cv_labels[i] << ' ';
      else if (i<num_cv+num_div)
	s << std::setw(12) << div_labels[i-num_cv] << ' ';
      else if (i<num_cv+num_div+num_dsv)
	s << std::setw(12) << dsv_labels[i-num_cv-num_div] << ' ';
      else
	s << std::setw(12) << drv_labels[i-num_cv-num_div-num_dsv] << ' ';
      for (j=0; j<numFns; ++j)
	s << std::setw(12) << simpleRankCorr(i,j) << ' ';
      s << '\n';
    }
  }

  if (partialRankCorr.numRows() == numVars &&
      partialRankCorr.numCols() == numFns) {
    s << "\nPartial Rank Correlation Matrix between input and output:\n"
      << "             ";
    for (j=0; j<numFns; ++j)
      s << std::setw(12) << resp_labels[j] << ' ';
    s << '\n';
    for (i=0; i<numVars; ++i) {
      if (i<num_cv)
	s << std::setw(12) << cv_labels[i] << ' ';
      else if (i<num_cv+num_div)
	s << std::setw(12) << div_labels[i-num_cv] << ' ';
      else if (i<num_cv+num_div+num_dsv)
	s << std::setw(12) << dsv_labels[i-num_cv-num_div] << ' ';
      else
	s << std::setw(12) << drv_labels[i-num_cv-num_div-num_dsv] << ' ';
      for (j=0; j<numFns; ++j)
	s << std::setw(12) << partialRankCorr(i,j) << ' ';
      s << '\n';
    }
  }

  //  This warning message has been supplanted by more generic tests for NaNs and
  //  Infs
  //if (numericalIssuesRank)
  //  s << "\nThere may be some numerical issues associated with the calculation "
  //    << "of the \npartial rank correlation coefficients above.  This can be "
  //    << "due to very small \nnumbers of input samples, or to ill-conditioned "
  //    << "matrices, \nin situations where the partials are very close to zero, "
  //    << "-1, or +1.\n";

  s << std::setprecision(write_precision)  // return to previous precision
    << std::endl;
}

} // namespace Dakota


#if !defined(PAWN_REXPR_HPP)
#define PAWN_REXPR_HPP

#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS

#include <boost/spirit/include/qi.hpp>
#include "rast.hpp"
#include "mexpr.hpp"
#include "sexpr.hpp"
#include "error_handler.hpp"
#include <vector>

namespace client { namespace relational { namespace parser
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    ///////////////////////////////////////////////////////////////////////////////
    //  The relational expression grammar
    ///////////////////////////////////////////////////////////////////////////////
    template <typename Iterator>
    struct expression : qi::grammar<Iterator, ast::expr(), ascii::space_type>
    {
        expression(error_handler<Iterator>& error_handler);

        qi::rule<Iterator, ast::expr(), ascii::space_type> expr;
        client::math::parser::expression<Iterator> mathExpr;
        client::str::parser::expression<Iterator> strExpr;
        qi::symbols<char, ast::optoken> relational_op;
    };
}}}

#endif


/*
 * ElevationVisualization.cpp
 *
 *  Created on: Jun 18, 2014
 *      Author: Péter Fankhauser, Christian Forster
 *   Institute: ETH Zurich, Autonomous Systems Lab
 */

#include "grid_map/GridMap.hpp"
#include "grid_map_visualization/visualizations/ElevationVisualization.hpp"
#include "grid_map_lib/GridMapMath.hpp"

// Eigen
#include <Eigen/Core>

namespace grid_map_visualization {

ElevationVisualization::ElevationVisualization(ros::NodeHandle& nodeHandle)
  : nodeHandle_(nodeHandle)
  , markerHeight_(1.0)
  , lowerColorValue_(255)
  , upperColorValue_(16711680)
  , lowerColor_(grid_map_visualization::color_utils::colorFromColorValue(lowerColorValue_))
  , upperColor_(grid_map_visualization::color_utils::colorFromColorValue(upperColorValue_))
{
  markerPublisher_ =
      nodeHandle_.advertise<visualization_msgs::Marker>("elevation", 1, true);
}

ElevationVisualization::~ElevationVisualization()
{}

bool ElevationVisualization::readParameters()
{
  nodeHandle_.param("elevation/marker_height", markerHeight_, markerHeight_);
  return true;
}

bool ElevationVisualization::initialize()
{
  marker_.ns = "elevation";
  marker_.lifetime = ros::Duration();
  marker_.action = visualization_msgs::Marker::ADD;
  marker_.type = visualization_msgs::Marker::CUBE_LIST;
  marker_.scale.z = markerHeight_;
  return true;
}

void ElevationVisualization::visualize(
    const grid_map::GridMap& map,
    const std::string& typeNameElevation,
    const std::string& typeNameColor,
    const float lowerValueBound,
    const float upperValueBound)
{
  // Set marker info.
  marker_.header.frame_id = map.getFrameId();
  marker_.header.stamp.fromNSec(map.getTimestamp());
  marker_.scale.x = map.getResolution();
  marker_.scale.y = map.getResolution();

  // Clear points.
  marker_.points.clear();
  marker_.colors.clear();

  float markerHeightOffset = static_cast<float>(markerHeight_/2.0);

  const Eigen::Array2i buffSize = map.getBufferSize();
  const Eigen::Array2i buffStartIndex = map.getBufferStartIndex();
  const bool haveColor = map.isType("color");
  for (unsigned int i = 0; i < buffSize(0); ++i)
  {
    for (unsigned int j = 0; j < buffSize(1); ++j)
    {
      // Getting map data.
      const Eigen::Array2i cellIndex(i, j);
      const Eigen::Array2i buffIndex =
          grid_map_lib::getBufferIndexFromIndex(cellIndex, buffSize, buffStartIndex);
      const float& elevation = map.at(typeNameElevation, buffIndex);
      if(std::isnan(elevation))
        continue;
      const float color = haveColor ? map.at(typeNameColor, buffIndex) : lowerValueBound;

      // Add marker point.
      Eigen::Vector2d position;
      map.getPosition(buffIndex, position);
      geometry_msgs::Point point;
      point.x = position.x();
      point.y = position.y();
      point.z = elevation - markerHeightOffset;
      marker_.points.push_back(point);

      // Add marker color.
      if(haveColor)
      {
        std_msgs::ColorRGBA markerColor =
            grid_map_visualization::color_utils::interpolateBetweenColors(
              color, lowerValueBound, upperValueBound, lowerColor_, upperColor_);
        marker_.colors.push_back(markerColor);
      }
    }
  }

  markerPublisher_.publish(marker_);
}

namespace color_utils {

Eigen::Vector3f colorValueToRGB(
    const unsigned long& colorValue)
{
  Eigen::Vector3i rgb;
  rgb(0) = (colorValue >> 16) & 0x0000ff;
  rgb(1) = (colorValue >> 8) & 0x0000ff;
  rgb(2) = (colorValue) & 0x0000ff;
  return rgb.cast<float>()/255.0;
}

std_msgs::ColorRGBA colorFromColorValue(
    const unsigned long& colorValue)
{
  Eigen::Vector3f colorVector = colorValueToRGB(colorValue);
  std_msgs::ColorRGBA color;
  color.r = colorVector(0);
  color.g = colorVector(1);
  color.b = colorVector(2);
  color.a = 1.0;
  return color;
}

float computeLinearMapping(
    const float& sourceValue,
    const float& sourceLowerValue,
    const float& sourceUpperValue,
    const float& mapLowerValue,
    const float& mapUpperValue)
{
  float m = (mapLowerValue - mapUpperValue) / (sourceLowerValue - sourceUpperValue);
  float b = mapUpperValue - m * sourceUpperValue;
  float mapValue = m * sourceValue + b;
  if (mapLowerValue < mapUpperValue)
  {
    mapValue = std::max(mapValue, mapLowerValue);
    mapValue = std::min(mapValue, mapUpperValue);
  }
  else
  {
    mapValue = std::min(mapValue, mapLowerValue);
    mapValue = std::max(mapValue, mapUpperValue);
  }
  return mapValue;
}

void setColorChannelFromValue(
    float& colorChannel,
    const float value,
    const float lowerValueBound,
    const float upperValueBound,
    const bool invert,
    const float colorChannelLowerValue,
    const float colorChannelUpperValue)
{
  float tempColorChannelLowerValue = colorChannelLowerValue;
  float tempColorChannelUpperValue = colorChannelUpperValue;
  if (invert)
  {
    tempColorChannelLowerValue = colorChannelUpperValue;
    tempColorChannelUpperValue = colorChannelLowerValue;
  }
  colorChannel =
      computeLinearMapping(
        value, lowerValueBound, upperValueBound, tempColorChannelLowerValue,
        tempColorChannelUpperValue);
}

std_msgs::ColorRGBA interpolateBetweenColors(
    const float value,
    const float lowerValueBound,
    const float upperValueBound,
    const std_msgs::ColorRGBA& colorForLowerValue,
    const std_msgs::ColorRGBA& colorForUpperValue)
{
  std_msgs::ColorRGBA color;
  color.a = 1.0;
  setColorChannelFromValue(
        color.r, value, lowerValueBound, upperValueBound,
        false, colorForLowerValue.r, colorForUpperValue.r);
  setColorChannelFromValue(
        color.g, value, lowerValueBound, upperValueBound,
        false, colorForLowerValue.g, colorForUpperValue.g);
  setColorChannelFromValue(
        color.b, value, lowerValueBound, upperValueBound,
        false, colorForLowerValue.b, colorForUpperValue.b);
  return color;
}

} /* namespace color_utils */
} /* namespace grid_map_visualization */


// Copyright (c) 2013 The Bitcoin Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/test/unit_test.hpp>
#include "entrustment.h"

BOOST_AUTO_TEST_SUITE(entrust_reward_tests)

BOOST_AUTO_TEST_CASE(test_activeuser) {
	Entrustment& ent = Entrustment::GetInstance();
	BlockHeight forkheight = ent.forkheight_halfMinDivideReward;

	BOOST_CHECK(ent.GetMinDivideReward(forkheight-1) == ent.GetMinDivideRewardV1());
	BOOST_CHECK(ent.GetMinDivideReward(forkheight) == ent.GetMinDivideRewardV2());
	BOOST_CHECK(ent.GetMinDivideReward(forkheight+1) == ent.GetMinDivideRewardV2());

	CAmount localMinDivideReward = ent.GetMinDivideReward(forkheight);

	dpos::UserStatistics user;
	user.entrustAmount = 1;
	BOOST_CHECK(ent.IsActiveUser(user,forkheight));

	user.entrustAmount = 0;
	user.totalDividendReward = 0;
	user.totalDealDivideReward = user.totalDividendReward + localMinDivideReward + 1;
	BOOST_CHECK(user.totalDealDivideReward - user.totalDividendReward > localMinDivideReward);
	BOOST_CHECK(ent.IsActiveUser(user,forkheight));

	user.totalDealDivideReward = user.totalDividendReward + localMinDivideReward;
	BOOST_CHECK(user.totalDealDivideReward - user.totalDividendReward == localMinDivideReward);
	BOOST_CHECK(ent.IsActiveUser(user,forkheight));

	user.totalDealDivideReward = user.totalDividendReward + localMinDivideReward - 1;
	BOOST_CHECK(user.totalDealDivideReward - user.totalDividendReward < localMinDivideReward);
	BOOST_CHECK(!ent.IsActiveUser(user,forkheight));
}

BOOST_AUTO_TEST_SUITE_END()


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*
    This example shows how to run a CNN based vehicle detector using dlib.  The
    example loads a pretrained model and uses it to find the rear ends of cars in
    an image.  We will also visualize some of the detector's processing steps by
    plotting various intermediate images on the screen.  Viewing these can help
    you understand how the detector works.

    The model used by this example was trained by the dnn_mmod_train_find_cars_ex.cpp
    example.  Also, since this is a CNN, you really should use a GPU to get the
    best execution speed.  For instance, when run on a NVIDIA 1080ti, this detector
    runs at 98fps when run on the provided test image.  That's more than an order
    of magnitude faster than when run on the CPU.

    Users who are just learning about dlib's deep learning API should read
    the dnn_introduction_ex.cpp and dnn_introduction2_ex.cpp examples to learn
    how the API works.  For an introduction to the object detection method you
    should read dnn_mmod_ex.cpp.

    You can also see some videos of this vehicle detector running on YouTube:
        https://www.youtube.com/watch?v=4B3bzmxMAZU
        https://www.youtube.com/watch?v=bP2SUo5vSlc
*/

#include <iostream>
#include <dlib/dnn.h>
#include <dlib/image_io.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_processing.h>

using namespace std;
using namespace dlib;

// The rear view vehicle detector network
template <long num_filters, typename SUBNET> using con5d = con<num_filters,5,5,2,2,SUBNET>;
template <long num_filters, typename SUBNET> using con5  = con<num_filters,5,5,1,1,SUBNET>;
template <typename SUBNET> using downsampler  = relu<affine<con5d<32, relu<affine<con5d<32, relu<affine<con5d<16,SUBNET>>>>>>>>>;
template <typename SUBNET> using rcon5  = relu<affine<con5<55,SUBNET>>>;
using net_type = loss_mmod<con<1,9,9,1,1,rcon5<rcon5<rcon5<downsampler<input_rgb_image_pyramid<pyramid_down<6>>>>>>>>;

// ----------------------------------------------------------------------------------------

int main() try
{
    net_type net;
    shape_predictor sp;
    // You can get this file from http://dlib.net/files/mmod_rear_end_vehicle_detector.dat.bz2
    // This network was produced by the dnn_mmod_train_find_cars_ex.cpp example program.
    // As you can see, the file also includes a separately trained shape_predictor.  To see
    // a generic example of how to train those refer to train_shape_predictor_ex.cpp.
    deserialize("mmod_rear_end_vehicle_detector.dat") >> net >> sp;

    matrix<rgb_pixel> img;
    load_image(img, "../mmod_cars_test_image.jpg");

    image_window win;
    win.set_image(img);

    // Run the detector on the image and show us the output.
    for (auto&& d : net(img))
    {
        // We use a shape_predictor to refine the exact shape and location of the detection
        // box.  This shape_predictor is trained to simply output the 4 corner points of
        // the box.  So all we do is make a rectangle that tightly contains those 4 points
        // and that rectangle is our refined detection position.
        auto fd = sp(img,d);
        rectangle rect;
        for (unsigned long j = 0; j < fd.num_parts(); ++j)
            rect += fd.part(j);
        win.add_overlay(rect, rgb_pixel(255,0,0));
    }

    cout << "Hit enter to view the intermediate processing steps" << endl;
    cin.get();

    // Now let's look at how the detector works.  The high level processing steps look like:
    //   1. Create an image pyramid and pack the pyramid into one big image.  We call this
    //      image the "tiled pyramid".
    //   2. Run the tiled pyramid image through the CNN.  The CNN outputs a new image where
    //      bright pixels in the output image indicate the presence of cars.
    //   3. Find pixels in the CNN's output image with a value > 0.  Those locations are your
    //      preliminary car detections.
    //   4. Perform non-maximum suppression on the preliminary detections to produce the
    //      final output.
    //
    // We will be plotting the images from steps 1 and 2 so you can visualize what's
    // happening.  For the CNN's output image, we will use the jet colormap so that "bright"
    // outputs, i.e. pixels with big values, appear in red and "dim" outputs appear as a
    // cold blue color.  To do this we pick a range of CNN output values for the color
    // mapping.  The specific values don't matter.  They are just selected to give a nice
    // looking output image.
    const float lower = -2.5;
    const float upper = 0.0;
    cout << "jet color mapping range:  lower="<< lower << "  upper="<< upper << endl;

    // Create a tiled pyramid image and display it on the screen.
    std::vector<rectangle> rects;
    matrix<rgb_pixel> tiled_img;
    // Get the type of pyramid the CNN used
    using pyramid_type = std::remove_reference<decltype(input_layer(net))>::type::pyramid_type;
    // And tell create_tiled_pyramid to create the pyramid using that pyramid type.
    create_tiled_pyramid<pyramid_type>(img, tiled_img, rects,
                                       input_layer(net).get_pyramid_padding(),
                                       input_layer(net).get_pyramid_outer_padding());
    image_window winpyr(tiled_img, "Tiled pyramid");

    // This CNN detector represents a sliding window detector with 3 sliding windows.  Each
    // of the 3 windows has a different aspect ratio, allowing it to find vehicles which
    // are either tall and skinny, squarish, or short and wide.  The aspect ratio of a
    // detection is determined by which channel in the output image triggers the detection.
    // Here we are just going to max pool the channels together to get one final image for
    // our display.  In this image, a pixel will be bright if any of the sliding window
    // detectors thinks there is a car at that location.
    cout << "Number of channels in final tensor image: " << net.subnet().get_output().k() << endl;
    matrix<float> network_output = image_plane(net.subnet().get_output(),0,0);
    for (long k = 1; k < net.subnet().get_output().k(); ++k)
        network_output = max_pointwise(network_output, image_plane(net.subnet().get_output(),0,k));
    // We will also upsample the CNN's output image.  The CNN we defined has an 8x
    // downsampling layer at the beginning. In the code below we are going to overlay this
    // CNN output image on top of the raw input image.  To make that look nice it helps to
    // upsample the CNN output image back to the same resolution as the input image, which
    // we do here.
    const double network_output_scale = img.nc()/(double)network_output.nc();
    resize_image(network_output_scale, network_output);

    // Display the network's output as a color image.
    image_window win_output(jet(network_output, upper, lower), "Output tensor from the network");

    // Also, overlay network_output on top of the tiled image pyramid and display it.
    for (long r = 0; r < tiled_img.nr(); ++r)
    {
        for (long c = 0; c < tiled_img.nc(); ++c)
        {
            dpoint tmp(c,r);
            tmp = input_tensor_to_output_tensor(net, tmp);
            tmp = point(network_output_scale*tmp);
            if (get_rect(network_output).contains(tmp))
            {
                float val = network_output(tmp.y(),tmp.x());
                // alpha blend the network output pixel with the RGB image to make our
                // overlay.
                rgb_alpha_pixel p;
                assign_pixel(p , colormap_jet(val,lower,upper));
                p.alpha = 120;
                assign_pixel(tiled_img(r,c), p);
            }
        }
    }
    // If you look at this image you can see that the vehicles have bright red blobs on
    // them.  That's the CNN saying "there is a car here!".  You will also notice there is
    // a certain scale at which it finds cars.  They have to be not too big or too small,
    // which is why we have an image pyramid.  The pyramid allows us to find cars of all
    // scales.
    image_window win_pyr_overlay(tiled_img, "Detection scores on image pyramid");

    // Finally, we can collapse the pyramid back into the original image.  The CNN doesn't
    // actually do this step, since it's enough to threshold the tiled pyramid image to get
    // the detections.  However, it makes a nice visualization and clearly indicates that
    // the detector is firing for all the cars.
    matrix<float> collapsed(img.nr(), img.nc());
    resizable_tensor input_tensor;
    input_layer(net).to_tensor(&img, &img+1, input_tensor);
    for (long r = 0; r < collapsed.nr(); ++r)
    {
        for (long c = 0; c < collapsed.nc(); ++c)
        {
            // Loop over a bunch of scale values and look up what part of network_output
            // corresponds to the point(c,r) in the original image, then take the max
            // detection score over all the scales and save it at pixel point(c,r).
            float max_score = -1e30;
            for (double scale = 1; scale > 0.2; scale *= 5.0/6.0)
            {
                // Map from input image coordinates to tiled pyramid coordinates.
                dpoint tmp = center(input_layer(net).image_space_to_tensor_space(input_tensor,scale, drectangle(dpoint(c,r))));
                // Now map from pyramid coordinates to network_output coordinates.
                tmp = point(network_output_scale*input_tensor_to_output_tensor(net, tmp));

                if (get_rect(network_output).contains(tmp))
                {
                    float val = network_output(tmp.y(),tmp.x());
                    if (val > max_score)
                        max_score = val;
                }
            }

            collapsed(r,c) = max_score;

            // Also blend the scores into the original input image so we can view it as
            // an overlay on the cars.
            rgb_alpha_pixel p;
            assign_pixel(p , colormap_jet(max_score,lower,upper));
            p.alpha = 120;
            assign_pixel(img(r,c), p);
        }
    }

    image_window win_collapsed(jet(collapsed, upper, lower), "Collapsed output tensor from the network");
    image_window win_img_and_sal(img, "Collapsed detection scores on raw image");

    cout << "Hit enter to end program" << endl;
    cin.get();
}
catch(image_load_error& e)
{
    cout << e.what() << endl;
    cout << "The test image is located in the examples folder.  So you should run this program from a sub folder so that the relative path is correct." << endl;
}
catch(serialization_error& e)
{
    cout << e.what() << endl;
    cout << "The correct model file can be obtained from: http://dlib.net/files/mmod_rear_end_vehicle_detector.dat.bz2" << endl;
}
catch(std::exception& e)
{
    cout << e.what() << endl;
}


// Copyright (c) 2015-2018 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#ifndef variational_bayes_mixture_model_hpp
#define variational_bayes_mixture_model_hpp

#include <array>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <cstddef>
#include <utility>
#include <cassert>
#include <limits>

#include <boost/optional.hpp>
#include <boost/math/special_functions/digamma.hpp>

#include "utils/maths.hpp"
#include "core/models/haplotype_likelihood_cache.hpp"

/**
 *
 * This file contains an implementation of the Variational Bayes mixture model
 * used by some genotype models. The notation follows the documentation.
 *
 */

namespace octopus { namespace model {

// Types needed for Variational Bayes model

struct VariationalBayesParameters
{
    double epsilon;
    unsigned max_iterations;
};

using ProbabilityVector    = std::vector<double>;
using LogProbabilityVector = std::vector<double>;

template <std::size_t K>
using VBAlpha = std::array<double, K>;
template <std::size_t K>
using VBAlphaVector = std::vector<VBAlpha<K>>;

class VBReadLikelihoodArray
{
public:
    using BaseType = HaplotypeLikelihoodCache::LikelihoodVector;

    VBReadLikelihoodArray() = default;

    explicit VBReadLikelihoodArray(const BaseType&);

    VBReadLikelihoodArray(const VBReadLikelihoodArray&)            = default;
    VBReadLikelihoodArray& operator=(const VBReadLikelihoodArray&) = default;
    VBReadLikelihoodArray(VBReadLikelihoodArray&&)                 = default;
    VBReadLikelihoodArray& operator=(VBReadLikelihoodArray&&)      = default;

    ~VBReadLikelihoodArray() = default;

    void operator=(const BaseType&);
    void operator=(std::reference_wrapper<const BaseType>);
    std::size_t size() const noexcept;
    BaseType::const_iterator begin() const noexcept;
    BaseType::const_iterator end() const noexcept;
    double operator[](const std::size_t n) const noexcept;

private:
    const BaseType* likelihoods;
};

template <std::size_t K>
using VBGenotype = std::array<VBReadLikelihoodArray, K>; // One element per haplotype in genotype (i.e. K)
template <std::size_t K>
using VBGenotypeVector = std::vector<VBGenotype<K>>; // Per element per genotype
template <std::size_t K>
using VBReadLikelihoodMatrix = std::vector<VBGenotypeVector<K>>; // One element per sample

template <std::size_t K>
using VBTau = std::array<double, K>; // One element per haplotype in genotype (i.e. K)
template <std::size_t K>
using VBResponsabilityVector = std::vector<VBTau<K>>; // One element per genotype
template <std::size_t K>
using VBResponsabilityMatrix = std::vector<VBResponsabilityVector<K>>; // One element per sample

template <std::size_t K>
struct VBLatents
{
    ProbabilityVector genotype_posteriors;
    LogProbabilityVector genotype_log_posteriors;
    VBAlphaVector<K> alphas;
    VBResponsabilityMatrix<K> responsabilities;
};

// Main VB method

template <std::size_t K>
std::pair<VBLatents<K>, double>
run_variational_bayes(const VBAlphaVector<K>& prior_alphas,
                      const LogProbabilityVector& genotype_log_priors,
                      const VBReadLikelihoodMatrix<K>& log_likelihoods,
                      const VariationalBayesParameters& params,
                      std::vector<LogProbabilityVector> seeds);

namespace detail {

using VBInverseLikelihood = std::vector<double>; // One element per genotype
using VBInverseGenotype = std::vector<VBInverseLikelihood>; // One element per read
template <std::size_t K>
using VBInverseGenotypeVector = std::array<VBInverseGenotype, K>; // One element per haplotype in genotype
template <std::size_t K>
using VBInverseReadLikelihoodMatrix = std::vector<VBInverseGenotypeVector<K>>; // One element per sample

template <std::size_t K>
auto invert(const VBGenotypeVector<K>& likelihoods)
{
    static_assert(K > 0, "K == 0");
    const auto num_genotypes = likelihoods.size();
    assert(num_genotypes > 0);
    const auto num_reads = likelihoods.front().front().size();
    VBInverseGenotypeVector<K> result {};
    for (std::size_t k {0}; k < K; ++k) {
        result[k] = VBInverseGenotype(num_reads, VBInverseLikelihood(num_genotypes));
        for (std::size_t n {0}; n < num_reads; ++n) {
            for (std::size_t g {0}; g < num_genotypes; ++g) {
                result[k][n][g] = likelihoods[g][k][n];
            }
        }
    }
    return result;
}

template <std::size_t K>
auto invert(const VBReadLikelihoodMatrix<K>& matrix)
{
    VBInverseReadLikelihoodMatrix<K> result {};
    result.reserve(matrix.size());
    std::transform(std::cbegin(matrix), std::cend(matrix), std::back_inserter(result),
                   [] (const auto& v) { return invert(v); });
    return result;
}

inline ProbabilityVector& exp(const LogProbabilityVector& log_probabilities, ProbabilityVector& result) noexcept
{
    std::transform(std::cbegin(log_probabilities), std::cend(log_probabilities), std::begin(result),
                   [] (const auto lp) noexcept { return std::exp(lp); });
    return result;
}

inline ProbabilityVector exp(const LogProbabilityVector& log_probabilities)
{
    ProbabilityVector result(log_probabilities.size());
    return exp(log_probabilities, result);
}

inline auto sum(const VBAlpha<2>& alpha) noexcept
{
    return alpha[0] + alpha[1];
}

inline auto sum(const VBAlpha<3>& alpha) noexcept
{
    return alpha[0] + alpha[1] + alpha[2];
}

template <std::size_t K>
auto sum(const VBAlpha<K>& alpha) noexcept
{
    return std::accumulate(std::cbegin(alpha), std::cend(alpha), 0.0);
}

template <typename T>
inline auto digamma_diff(const T a, const T b)
{
    using boost::math::digamma;
    return digamma(a) - digamma(b);
}

template <typename T>
inline T log_sum_exp(const std::array<T, 1>& logs)
{
    return logs[0];
}

template <typename T>
inline T log_sum_exp(const std::array<T, 2>& logs)
{
    return maths::log_sum_exp(logs[0], logs[1]);
}

template <typename T>
inline T log_sum_exp(const std::array<T, 3>& logs)
{
    return maths::log_sum_exp(logs[0], logs[1], logs[2]);
}

template <typename T, std::size_t K>
T log_sum_exp(const std::array<T, K>& logs)
{
    return maths::log_sum_exp(logs);
}

template <std::size_t K>
auto count_reads(const VBGenotypeVector<K>& likelihoods) noexcept
{
    return likelihoods[0][0].size();
}

template <std::size_t K>
auto count_reads(const VBInverseGenotypeVector<K>& likelihoods) noexcept
{
    return likelihoods[0].size();
}

template <std::size_t K>
auto marginalise(const ProbabilityVector& distribution, const VBGenotypeVector<K>& likelihoods,
                 const unsigned k, const std::size_t n) noexcept
{
    return std::inner_product(std::cbegin(distribution), std::cend(distribution),
                              std::cbegin(likelihoods), 0.0, std::plus<> {},
                              [k, n] (const auto p, const auto& haplotype_likelihoods) noexcept {
                                  return p * haplotype_likelihoods[k][n];
                              });
}

template <std::size_t K>
auto marginalise(const ProbabilityVector& distribution, const VBInverseGenotypeVector<K>& likelihoods,
                 const unsigned k, const std::size_t n) noexcept
{
    return std::inner_product(std::cbegin(distribution), std::cend(distribution), std::cbegin(likelihoods[k][n]), 0.0);
}

template <std::size_t K, typename VBLikelihoodVector_>
VBResponsabilityVector<K>
init_responsabilities(const VBAlpha<K>& prior_alphas,
                      const ProbabilityVector& genotype_probabilities,
                      const VBLikelihoodVector_& read_likelihoods)
{
    using T = typename VBAlpha<K>::value_type;
    std::array<T, K> al; // no need to keep recomputing this
    const auto a0 = sum(prior_alphas);
    for (unsigned k {0}; k < K; ++k) {
        al[k] = digamma_diff(prior_alphas[k], a0);
    }
    const auto N = count_reads(read_likelihoods);
    VBResponsabilityVector<K> result(N);
    std::array<T, K> ln_rho;
    for (std::size_t n {0}; n < N; ++n) {
        for (unsigned k {0}; k < K; ++k) {
            ln_rho[k] = al[k] + marginalise(genotype_probabilities, read_likelihoods, k, n);
        }
        const auto ln_rho_norm = log_sum_exp(ln_rho);
        for (unsigned k {0}; k < K; ++k) {
            result[n][k] = std::exp(ln_rho[k] - ln_rho_norm);
        }
    }
    return result;
}

template <std::size_t K, typename VBLikelihoodMatrix>
VBResponsabilityMatrix<K>
init_responsabilities(const VBAlphaVector<K>& prior_alphas,
                      const ProbabilityVector& genotype_probabilities,
                      const VBLikelihoodMatrix& read_likelihoods)
{
    const auto S = read_likelihoods.size(); // num samples
    VBResponsabilityMatrix<K> result{};
    result.reserve(S);
    for (std::size_t s {0}; s < S; ++s) {
        result.push_back(init_responsabilities(prior_alphas[s], genotype_probabilities, read_likelihoods[s]));
    }
    return result;
}

template <std::size_t K, typename VBLikelihoodVector_>
void update_responsabilities(VBResponsabilityVector<K>& result,
                             const VBAlpha<K>& posterior_alphas,
                             const ProbabilityVector& genotype_probabilities,
                             const VBLikelihoodVector_& read_likelihoods)
{
    using T = typename VBAlpha<K>::value_type;
    std::array<T, K> al;
    const auto a0 = sum(posterior_alphas);
    for (unsigned k {0}; k < K; ++k) {
        al[k] = digamma_diff(posterior_alphas[k], a0);
    }
    const auto N = count_reads(read_likelihoods);
    std::array<T, K> ln_rho;
    for (std::size_t n {0}; n < N; ++n) {
        for (unsigned k {0}; k < K; ++k) {
            ln_rho[k] = al[k] + marginalise(genotype_probabilities, read_likelihoods, k, n);
        }
        const auto ln_rho_norm = log_sum_exp(ln_rho);
        for (unsigned k {0}; k < K; ++k) {
            result[n][k] = std::exp(ln_rho[k] - ln_rho_norm);
        }
    }
}

// same as init_responsabilities but in-place
template <std::size_t K, typename VBLikelihoodMatrix>
void update_responsabilities(VBResponsabilityMatrix<K>& result,
                             const VBAlphaVector<K>& posterior_alphas,
                             const ProbabilityVector& genotype_probabilities,
                             const VBLikelihoodMatrix& read_likelihoods)
{
    const auto S = read_likelihoods.size();
    for (std::size_t s {0}; s < S; ++s) {
        update_responsabilities(result[s], posterior_alphas[s], genotype_probabilities, read_likelihoods[s]);
    }
}

template <std::size_t K>
auto sum(const VBResponsabilityVector<K>& taus, const unsigned k) noexcept
{
    return std::accumulate(std::cbegin(taus), std::cend(taus), 0.0,
                           [k] (const auto curr, const auto& tau) noexcept {
                               return curr + tau[k];
                           });
}

template <std::size_t K>
void update_alpha(VBAlpha<K>& alpha, const VBAlpha<K>& prior_alpha,
                  const VBResponsabilityVector<K>& taus) noexcept
{
    for (unsigned k {0}; k < K; ++k) {
        alpha[k] = prior_alpha[k] + sum(taus, k);
    }
}

template <std::size_t K>
void update_alphas(VBAlphaVector<K>& alphas, const VBAlphaVector<K>& prior_alphas,
                   const VBResponsabilityMatrix<K>& responsabilities) noexcept
{
    const auto S = alphas.size();
    assert(S == prior_alphas.size() && S == responsabilities.size());
    for (std::size_t s {0}; s < S; ++s) {
        update_alpha(alphas[s], prior_alphas[s], responsabilities[s]);
    }
}

template <std::size_t K>
auto marginalise(const VBResponsabilityVector<K>& responsabilities,
                 const VBGenotypeVector<K>& read_likelihoods,
                 const std::size_t g) noexcept
{
    double result {0};
    const auto N = read_likelihoods[0][0].size(); // num reads in sample s
    assert(responsabilities.size() == N);
    assert(responsabilities[0].size() == K && read_likelihoods[g].size() == K);
    for (unsigned k {0}; k < K; ++k) {
        const auto& k_likelihoods = read_likelihoods[g][k];
        for (std::size_t n {0}; n < N; ++n) {
            result += responsabilities[n][k] * k_likelihoods[n];
        }
    }
    return result;
}

template <std::size_t K>
auto marginalise(const VBResponsabilityMatrix<K>& responsabilities,
                 const VBReadLikelihoodMatrix<K>& read_likelihoods,
                 const std::size_t g) noexcept
{
    double result {0};
    const auto S = read_likelihoods.size(); // num samples
    assert(S == responsabilities.size());
    for (std::size_t s {0}; s < S; ++s) {
        result += marginalise(responsabilities[s], read_likelihoods[s], g);
    }
    return result;
}

template <std::size_t K>
void update_genotype_log_posteriors(LogProbabilityVector& result,
                                    const LogProbabilityVector& genotype_log_priors,
                                    const VBResponsabilityMatrix<K>& responsabilities,
                                    const VBReadLikelihoodMatrix<K>& read_likelihoods)
{
    const auto G = result.size();
    for (std::size_t g {0}; g < G; ++g) {
        result[g] = genotype_log_priors[g] + marginalise(responsabilities, read_likelihoods, g);
    }
    maths::normalise_logs(result);
}

inline auto max_change(const VBAlpha<2>& lhs, const VBAlpha<2>& rhs) noexcept
{
    return std::max(std::abs(lhs.front() - rhs.front()), std::abs(lhs.back() - rhs.back()));
}

inline auto max_change(const VBAlpha<3>& lhs, const VBAlpha<3>& rhs) noexcept
{
    return std::max({std::abs(lhs[0] - rhs[0]), std::abs(lhs[1] - rhs[1]), std::abs(lhs[2] - rhs[2])});
}

template <std::size_t K>
auto max_change(const VBAlpha<K>& lhs, const VBAlpha<K>& rhs) noexcept
{
    double result {0};
    for (std::size_t k {0}; k < K; ++k) {
        const auto curr = std::abs(lhs[k] - rhs[k]);
        if (curr > result) result = curr;
    }
    return result;
}

template <std::size_t K>
auto max_change(const VBAlphaVector<K>& prior_alphas, const VBAlphaVector<K>& posterior_alphas) noexcept
{
    const auto S = prior_alphas.size();
    assert(S == posterior_alphas.size());
    double result {0};
    for (std::size_t s {0}; s < S; ++s) {
        const auto curr = max_change(prior_alphas[s], posterior_alphas[s]);
        if (curr > result) result = curr;
    }
    return result;
}

template <std::size_t K>
std::pair<bool, double> check_convergence(const VBAlphaVector<K>& prior_alphas,
                                          const VBAlphaVector<K>& posterior_alphas,
                                          const double prev_max_change,
                                          const double epsilon) noexcept
{
    const auto new_max_change = max_change(prior_alphas, posterior_alphas);
    return std::make_pair(std::abs(new_max_change - prev_max_change) < epsilon, new_max_change);
}

template <std::size_t K>
auto entropy(const VBTau<K>& tau) noexcept
{
    return -std::accumulate(std::cbegin(tau), std::cend(tau), 0.0,
                            [] (const auto curr, const auto t) noexcept { return curr + (t * std::log(t)); });
}

// E [ln q(Z_s)]
template <std::size_t K>
auto sum_entropies(const VBResponsabilityVector<K>& taus) noexcept
{
    return std::accumulate(std::cbegin(taus), std::cend(taus), 0.0,
                           [] (const auto curr, const auto& tau) noexcept { return curr + entropy(tau); });
}

template <std::size_t K>
auto calculate_evidence_lower_bound(const VBAlphaVector<K>& prior_alphas,
                                    const VBAlphaVector<K>& posterior_alphas,
                                    const LogProbabilityVector& genotype_log_priors,
                                    const ProbabilityVector& genotype_posteriors,
                                    const LogProbabilityVector& genotype_log_posteriors,
                                    const VBResponsabilityMatrix<K>& taus,
                                    const VBReadLikelihoodMatrix<K>& log_likelihoods,
                                    const boost::optional<double> max_posterior_skip = boost::none)
{
    const auto G = genotype_log_priors.size();
    const auto S = log_likelihoods.size();
    double result {0};
    for (std::size_t g {0}; g < G; ++g) {
        if (!max_posterior_skip || genotype_posteriors[g] >= *max_posterior_skip) {
            auto w = genotype_log_priors[g] - genotype_log_posteriors[g];
            for (std::size_t s {0}; s < S; ++s) {
                const auto N_s = taus[s].size();
                for (std::size_t k {0}; k < K; ++k) {
                    for (std::size_t n {0}; n < N_s; ++n) {
                        w += taus[s][n][k] * log_likelihoods[s][g][k][n];
                    }
                }
            }
            result += genotype_posteriors[g] * w;
        }
    }
    for (std::size_t s {0}; s < S; ++s) {
        result += (maths::log_beta(posterior_alphas[s]) - maths::log_beta(prior_alphas[s]));
        result += sum_entropies(taus[s]);
    }
    return result;
}

// Main algorithm - single seed

// Starting iteration with given genotype_log_posteriors
template <std::size_t K, typename VBLikelihoodMatrix1, typename VBLikelihoodMatrix2>
VBLatents<K>
run_variational_bayes(const VBAlphaVector<K>& prior_alphas,
                      const LogProbabilityVector& genotype_log_priors,
                      const VBLikelihoodMatrix1& log_likelihoods1,
                      const VBLikelihoodMatrix2& log_likelihoods2,
                      LogProbabilityVector genotype_log_posteriors,
                      const VariationalBayesParameters& params)
{
    assert(!prior_alphas.empty());
    assert(!genotype_log_priors.empty());
    assert(!log_likelihoods1.empty());
    assert(log_likelihoods1.size() == log_likelihoods2.size());
    assert(prior_alphas.size() == log_likelihoods1.size()); // num samples
    assert(log_likelihoods1.front().size() == genotype_log_priors.size()); // num genotypes
    assert(params.max_iterations > 0);
    auto genotype_posteriors = exp(genotype_log_posteriors);
    auto posterior_alphas = prior_alphas;
    auto responsabilities = init_responsabilities<K>(posterior_alphas, genotype_posteriors, log_likelihoods2);
    assert(responsabilities.size() == log_likelihoods1.size()); // num samples
    auto prev_evidence = std::numeric_limits<double>::lowest();
    bool is_converged {};
    double max_change {};
    for (unsigned i {0}; i < params.max_iterations; ++i) {
        update_genotype_log_posteriors(genotype_log_posteriors, genotype_log_priors, responsabilities, log_likelihoods1);
        exp(genotype_log_posteriors, genotype_posteriors);
        update_alphas(posterior_alphas, prior_alphas, responsabilities);
        update_responsabilities(responsabilities, posterior_alphas, genotype_posteriors, log_likelihoods2);
        std::tie(is_converged, max_change) = check_convergence(prior_alphas, posterior_alphas, max_change, params.epsilon);
        if (is_converged) break;
        auto curr_evidence = calculate_evidence_lower_bound(prior_alphas, posterior_alphas, genotype_log_priors,
                                                            genotype_posteriors, genotype_log_posteriors, responsabilities,
                                                            log_likelihoods1, 1e-10);
        if (curr_evidence <= prev_evidence || (curr_evidence - prev_evidence) < params.epsilon) break;
        prev_evidence = curr_evidence;
    }
    return VBLatents<K> {
        std::move(genotype_posteriors), std::move(genotype_log_posteriors),
        std::move(posterior_alphas), std::move(responsabilities)
    };
}

// Not using inverted log likelihoods
template <std::size_t K>
VBLatents<K>
run_variational_bayes(const VBAlphaVector<K>& prior_alphas,
                      const LogProbabilityVector& genotype_log_priors,
                      const VBReadLikelihoodMatrix<K>& log_likelihoods,
                      LogProbabilityVector genotype_log_posteriors,
                      const VariationalBayesParameters& params)
{
    return run_variational_bayes(prior_alphas, genotype_log_posteriors, log_likelihoods,
                                 log_likelihoods, genotype_log_posteriors, params);
}

// Main algorithm - multiple seed

template <std::size_t K>
bool run_vb_with_matrix_inversion(const VBReadLikelihoodMatrix<K>& log_likelihoods,
                                  const VariationalBayesParameters& params,
                                  const std::vector<LogProbabilityVector>& seeds) noexcept
{
    return true;
}

template <std::size_t K>
std::vector<VBLatents<K>>
run_variational_bayes(const VBAlphaVector<K>& prior_alphas,
                      const LogProbabilityVector& genotype_log_priors,
                      const VBReadLikelihoodMatrix<K>& log_likelihoods,
                      const VariationalBayesParameters& params,
                      std::vector<LogProbabilityVector>&& seeds)
{
    std::vector<VBLatents<K>> result {};
    result.reserve(seeds.size());
    if (run_vb_with_matrix_inversion(log_likelihoods, params, seeds)) {
        const auto inverted_log_likelihoods = invert(log_likelihoods);
        for (auto& seed : seeds) {
            result.push_back(detail::run_variational_bayes(prior_alphas, genotype_log_priors,
                                                           log_likelihoods, inverted_log_likelihoods,
                                                           std::move(seed), params));
        }
    } else {
        for (auto& seed : seeds) {
            result.push_back(detail::run_variational_bayes(prior_alphas, genotype_log_priors,
                                                           log_likelihoods,
                                                           std::move(seed), params));
        }
    }
    return result;
}

// lower-bound calculation

template <std::size_t K>
auto calculate_evidence_lower_bound(const VBAlphaVector<K>& prior_alphas,
                                    const LogProbabilityVector& genotype_log_priors,
                                    const VBReadLikelihoodMatrix<K>& log_likelihoods,
                                    const VBLatents<K>& latents)
{
    return calculate_evidence_lower_bound(prior_alphas, latents.alphas, genotype_log_priors,
                                          latents.genotype_posteriors, latents.genotype_log_posteriors,
                                          latents.responsabilities, log_likelihoods);

}

template <std::size_t K>
std::pair<VBLatents<K>, double>
get_max_evidence_latents(const VBAlphaVector<K>& prior_alphas,
                         const LogProbabilityVector& genotype_log_priors,
                         const VBReadLikelihoodMatrix<K>& log_likelihoods,
                         std::vector<VBLatents<K>>&& latents)
{
    std::vector<double> seed_evidences(latents.size());
    std::transform(std::cbegin(latents), std::cend(latents), std::begin(seed_evidences),
                   [&] (const auto& seed_latents) {
                       return calculate_evidence_lower_bound(prior_alphas, genotype_log_priors, log_likelihoods, seed_latents);
                   });
    const auto max_itr = std::max_element(std::cbegin(seed_evidences), std::cend(seed_evidences));
    const auto max_idx = std::distance(std::cbegin(seed_evidences), max_itr);
    return std::make_pair(std::move(latents[max_idx]), *max_itr);
}

} // namespace detail

template <std::size_t K>
std::pair<VBLatents<K>, double>
run_variational_bayes(const VBAlphaVector<K>& prior_alphas,
                      const LogProbabilityVector& genotype_log_priors,
                      const VBReadLikelihoodMatrix<K>& log_likelihoods,
                      const VariationalBayesParameters& params,
                      std::vector<LogProbabilityVector> seeds)
{
    assert(!seeds.empty());
    auto latents = detail::run_variational_bayes(prior_alphas, genotype_log_priors, log_likelihoods, params, std::move(seeds));
    return detail::get_max_evidence_latents(prior_alphas, genotype_log_priors, log_likelihoods, std::move(latents));
}

inline VBReadLikelihoodArray::VBReadLikelihoodArray(const BaseType& underlying_likelihoods)
: likelihoods{std::addressof(underlying_likelihoods)} {}

inline void VBReadLikelihoodArray::operator=(const BaseType& other)
{
    likelihoods = std::addressof(other);
}

inline void VBReadLikelihoodArray::operator=(std::reference_wrapper<const BaseType> other)
{
    likelihoods = std::addressof(other.get());
}

inline std::size_t VBReadLikelihoodArray::size() const noexcept
{
    return likelihoods->size();
}

inline VBReadLikelihoodArray::BaseType::const_iterator VBReadLikelihoodArray::begin() const noexcept
{
    return likelihoods->begin();
}

inline VBReadLikelihoodArray::BaseType::const_iterator VBReadLikelihoodArray::end() const noexcept
{
    return likelihoods->end();
}

inline double VBReadLikelihoodArray::operator[](const std::size_t n) const noexcept
{
    return likelihoods->operator[](n);
}

} // namespace model
} // namespace octopus

#endif


#include <boost/crc.hpp>
#include "node_id.h"

using namespace ouinet::bittorrent;

static bool get_rbit(const NodeID::Buffer& buffer, size_t n) {
    return (buffer[n / CHAR_BIT] & (1 << (CHAR_BIT - 1 - (n % CHAR_BIT)))) != 0;
}

static void set_rbit(NodeID::Buffer& buffer, size_t n, bool value) {
    char bit = 1 << (CHAR_BIT - (n % CHAR_BIT) - 1);

    if (value) buffer[n / CHAR_BIT] |=  bit;
    else       buffer[n / CHAR_BIT] &= ~bit;
}

static const NodeID::Buffer& zero_buffer() {
    static bool was_zeroed = false;
    static NodeID::Buffer buf;

    if (!was_zeroed) {
        memset(buf.data(), 0, buf.size());
        was_zeroed = true;
    }

    return buf;
}

bool NodeID::bit(int n) const
{
    return get_rbit(buffer, n);
}

void NodeID::set_bit(int n, bool value)
{
    set_rbit(buffer, n, value);
}

const NodeID::Range& NodeID::Range::max()
{
    static Range max_range{ zero_buffer(), 0 };
    return max_range;
}

NodeID NodeID::Range::random_id() const
{
    // XXX: Use std::uniform_int_distribution instead of std::rand

    size_t s_bytes = mask / CHAR_BIT;
    size_t s_bits  = mask % CHAR_BIT;

    NodeID ret;

    for (size_t i = 0; i < ret.buffer.size(); i++) {
        if (i < s_bytes) {
            ret.buffer[i] = stencil[i];
        }
        else if (i > s_bytes) {
            ret.buffer[i] = rand() & 0xff;
        }
        else {
            ret.buffer[i] = (stencil[i] & ((0xff << (CHAR_BIT - s_bits)) & 0xff))
                          | (rand() & ((1 << (CHAR_BIT - s_bits)) - 1));
        }
    }

    return ret;
}

NodeID::Range NodeID::Range::reduce(bool bit) const {
    Range ret{stencil, mask};
    ++ret.mask;
    set_rbit(ret.stencil, ret.mask, bit);
    return ret;
}

std::string NodeID::to_hex() const
{
    std::string output;
    for (unsigned int i = 0; i < buffer.size(); i++) {
        const char* digits = "0123456789abcdef";
        output += digits[(buffer[i] >> 4) & 0xf];
        output += digits[(buffer[i] >> 0) & 0xf];
    }
    return output;
}

std::string NodeID::to_bytestring() const
{
    return std::string((char*) buffer.data(), buffer.size());
}

NodeID NodeID::from_bytestring(const std::string& bytestring)
{
    NodeID output;
    std::copy(bytestring.begin(), bytestring.end(), output.buffer.begin());
    return output;
}

const NodeID& NodeID::zero()
{
    static const NodeID ret = from_bytestring(std::string(20, '\0'));
    return ret;
}

NodeID NodeID::generate(asio::ip::address address)
{
    return generate(address, boost::none);
}

NodeID NodeID::generate( asio::ip::address address
                       , boost::optional<uint8_t> test_rnd)
{
    /*
     * Choose DHT ID based on ip address.
     * See: BEP 42
     */

    NodeID node_id;

    using CRC32C = boost::crc_optimal<32, 0x1edc6f41, 0xffffffff, 0xffffffff, true, true>;

    uint32_t checksum;

    node_id.buffer[19] = (test_rnd ? *test_rnd : std::rand()) & 0xff;

    if (address.is_v4()) {
        auto ip_bytes = address.to_v4().to_bytes();

        for (int i = 0; i < 4; i++) {
            ip_bytes[i] &= (0xff >> (6 - i * 2));
        }

        ip_bytes[0] |= ((node_id.buffer[19] & 7) << 5);

        CRC32C crc;
        crc.process_bytes(ip_bytes.data(), 4);
        checksum = crc.checksum();
    } else {
        auto ip_bytes = address.to_v6().to_bytes();

        for (int i = 0; i < 8; i++) {
            ip_bytes[i] &= (0xff >> (7 - i));
        }

        ip_bytes[0] |= ((node_id.buffer[19] & 7) << 5);

        CRC32C crc;
        crc.process_bytes(ip_bytes.data(), 8);
        checksum = crc.checksum();
    }

    node_id.buffer[0] = (checksum >> 24) & 0xff;
    node_id.buffer[1] = (checksum >> 16) & 0xff;
    node_id.buffer[2] = ((checksum >> 8) & 0xf8) | (rand() & 0x7);
    for (int i = 3; i < 19; i++) {
        node_id.buffer[i] = rand() & 0xff;
    }

    return node_id;
}


#ifndef FIELD_H
#define FIELD_H

#include "vector"
#include "array"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wmissing-braces"
#include "dccrg.hpp"
#include "dccrg_cartesian_geometry.hpp"
#pragma clang diagnostic pop

#include "cell.hpp"
#include "parameters.hpp"
#include "common.h"

#include <Eigen/Dense>

using namespace std;
using namespace dccrg;
using namespace Eigen;

typedef Array<double, 4, 1> vec4;
// typedef Array<double, 3, 1> vec3;
typedef Array<double, 4, 4> mat4;

// class to handle neighborhood cells
class Field_Cube
{
public:

    std::array<vec4, 27> fields;

    vec4& operator [](const std::size_t i){return this->fields[i];}
    const vec4& operator [](const std::size_t i) const {return this->fields[i];}

    void init_currents()
    {
        for(int ijk=0; ijk<27; ijk++) {
            fields[ijk] = vec4::Zero();
        }
        return;
    };

    int index(int i, int j, int k) {
        return (i+1) + (j+1)*3 + (k+1)*3*3;
    };

    void add_J(int i, int j, int k, vec4 dJ)
    {
        int ijk = this->index(i,j,k);

        #ifdef DEBUG
        cout << "  addJ: " << i << j << k << " -> " << ijk
        << " A" << dJ << " B " << fields[ijk] << endl;
        #endif

        fields[ijk] += dJ;
    };

    vec4 J(int i, int j, int k) {
        return this->fields[index(i,j,k)];
    };

    void distribute(dccrg::Dccrg<Cell, dccrg::Cartesian_Geometry>& grid, uint64_t cell);

};

class Field_Solver
{
public:

    // Mesh() {};

    // MPI parameters
    int
        rank = 0;

    // Advance half of the B field
    void push_half_B(dccrg::Dccrg<Cell, dccrg::Cartesian_Geometry>& grid);

    // Advance E field
	void push_E(dccrg::Dccrg<Cell, dccrg::Cartesian_Geometry>& grid);

};

#endif


//          Copyright Gavin Band 2008 - 2012.
// 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)

#include <string>
#include <memory>
#include <set>
#include <map>
#include <utility>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
#include <boost/regex.hpp>
#include <boost/math/distributions/hypergeometric.hpp>
#include "appcontext/CmdLineOptionProcessor.hpp"
#include "appcontext/ApplicationContext.hpp"
#include "appcontext/ProgramFlow.hpp"
#include "appcontext/get_current_time_as_string.hpp"
#include "genfile/FileUtils.hpp"
#include "genfile/utility.hpp"
#include "statfile/BuiltInTypeStatSource.hpp"
#include <Eigen/Core>

namespace globals {
	std::string const program_name = "overrep" ;
	std::string const program_version = "0.1" ;
}

struct OverrepOptions: public appcontext::CmdLineOptionProcessor {
	std::string get_program_name() const { return globals::program_name ; }

	void declare_options( appcontext::OptionProcessor& options ) {
		options.set_help_option( "-help" ) ;

		options.declare_group( "File handling options" ) ;
		options[ "-p" ]
			.set_description( "Specify the path of a file containing pathway definitions to load."
			 	" This file must have at least three columns; the first should be an identifier for the pathway (no whitespace),"
				" the second should be the pathway name, and the third should contain gene identifiers." )
			.set_takes_values_until_next_option()
			.set_minimum_multiplicity( 1 )
			.set_maximum_multiplicity( 100 )
		;

		options[ "-c" ]
			.set_description( "Specify the path of a file containing gene clusters to load." )
			.set_takes_single_value()
		;

		options[ "-u" ]
			.set_description( "Specify the path of a file containing a list of genes to treat as the gene \"universe\"." )
			.set_takes_single_value()
			.set_is_required()
		;

		options[ "-g" ]
			.set_description( "Specify the path of a file containing a list of genes to test with." )
			.set_takes_single_value()
		;

		options.declare_group( "Miscellaneous options" ) ;
		options[ "-P-value" ]
			.set_description( "Output lists of genes for all pathways getting this P-value or better." )
			.set_takes_single_value()
			.set_default_value( 0.000001 )
		;
		options[ "-analysis-name" ]
			.set_description( "Specify a name to label results from this analysis with.  (This applies to modules which store their results in a qcdb file.)" )
			.set_takes_single_value()
			.set_is_required() ;

		options[ "-intersect" ]
			.set_description( "Specify that the universe should only include genes that actuall occur in pathways." ) ;

		options[ "-log" ]
			.set_description( "Specify the path of the log file." )
			.set_takes_single_value()
			.set_default_value( "overrep.log" ) ;
	}
} ;

// Fisher's exact test between categories
//               red           black
//     good |     a      |       b        |
//      bad |     c      |       d        |
//
// Under the null that both rows are distributed the same, a is hypergeometrically
// distributed.  Fishers' p-value is then the mass under the hypergeometric distribution
// of all values of a greater than or equal to the one given that are consistent with
// the given margins of the table.
struct FishersExactTest {
	FishersExactTest( Eigen::Matrix2d const& matrix ):
		m_matrix( matrix ),
		m_distribution( m_matrix.col( 0 ).sum(), m_matrix.row( 0 ).sum(), m_matrix.sum() )
	{}

	double get_OR() const {
		return ( m_matrix(0,0) * m_matrix(1,1) ) / ( m_matrix(0,1)*m_matrix(1,0) ) ;
	}

	std::pair< double, double > get_confidence_interval() const {
		assert(0) ;
	}

	double get_pvalue() const {
		using boost::math::cdf ;
		using boost::math::complement ;

		if( m_matrix(0,0) == 0 ) {
			return 1 ;
		} else {
			return cdf( complement( m_distribution, m_matrix( 0, 0 ) - 1.0 )) ;
		}
	}

	private:
		Eigen::Matrix2d const m_matrix ;
		boost::math::hypergeometric m_distribution ;
} ;

void load_list_of_strings( std::string const& filename, boost::function< void ( std::string const& ) > setter ) {
	std::auto_ptr< std::istream > f = genfile::open_text_file_for_input( filename ) ;
	std::string element ;
	while( (*f) >> element ) {
		setter( element ) ;
	}
}

void load_pathways_list(
	statfile::BuiltInTypeStatSource& source,
	boost::function< void ( std::string const&, std::string const& ) > set_pathway_name,
	boost::function< void ( std::string const&, std::string const& ) > set_pathway_gene
) {
	std::string pathway_id ;
	std::string pathway_name ;
	std::string gene_id ;
	while( source >> pathway_id ) {
		source >> pathway_name >> gene_id >> statfile::ignore_all() ;
		set_pathway_name( pathway_id, pathway_name ) ;
		set_pathway_gene( pathway_id, gene_id ) ;
	}
}

void load_clusters(
	statfile::BuiltInTypeStatSource& source,
	boost::function< void ( std::string const&, std::string const& ) > set_cluster_member = boost::function< void ( std::string const&, std::string const& ) >(),
	boost::function< void ( std::string const&, std::string const& ) > set_cluster_comment = boost::function< void ( std::string const&, std::string const& ) >()
) {
	std::string cluster_id ;
	std::string cluster_comment ;
	std::string gene_regexp ;
	while( source >> cluster_id ) {
		source >> gene_regexp >> cluster_comment >> statfile::ignore_all() ;
		if( set_cluster_comment ) {
			set_cluster_comment( cluster_id, cluster_comment ) ;
		}
		if( set_cluster_member ) {
			set_cluster_member( cluster_id, gene_regexp ) ;
		}
	}
}

namespace impl {
	struct MapSetter {
		typedef std::string A ;
		typedef std::string B ;
		typedef std::map< A, B > Map ;
		MapSetter( Map& map ): m_map( &map ) {} ;
		MapSetter( MapSetter const& other ): m_map( other.m_map ) {} ;
		MapSetter& operator=( MapSetter const& other ) {
			m_map = other.m_map ;
			return *this ;
		}

		void operator()( std::string const& left, std::string const& right ) const {
			(*m_map)[ left ] = right ;
		}

	private:
		Map* m_map ;
	} ;

	struct MapSetSetter {
		typedef std::string A ;
		typedef std::set< std::string > B ;
		typedef std::map< A, B > Map ;
		MapSetSetter( Map& map ): m_map( &map ) {} ;
		MapSetSetter( MapSetSetter const& other ): m_map( other.m_map ) {} ;
		MapSetSetter& operator=( MapSetSetter const& other ) {
			m_map = other.m_map ;
			return *this ;
		}

		void operator()( std::string const& left, std::string const& right ) const {
			(*m_map)[ left ].insert( right ) ;
		}

	private:
		mutable Map* m_map ;
	} ;

	template< typename Container >
	void insert_into( typename Container::value_type const& value, Container* X ) {
		X->insert( value ) ;
	}

	template< typename Container >
	bool in( typename Container::value_type const& value, Container const& X ) {
		return X.find( value ) != X.end() ;
	}
}

struct OverrepApplication: public appcontext::ApplicationContext {
public:
	OverrepApplication( int argc, char **argv ):
		appcontext::ApplicationContext(
			globals::program_name,
			globals::program_version,
			std::auto_ptr< appcontext::OptionProcessor >( new OverrepOptions ),
			argc,
			argv,
			"-log"
		)
	{}

	void process() {
		unsafe_process() ;
	}

private:
	typedef std::set< std::string > StringSet ;
	typedef std::map< std::string, std::string > StringStringMap ;
	typedef std::map< std::string, std::set< std::string > > StringStringSetMap ;

	void unsafe_process() {
		load_data() ;
		m_cluster_mapping = get_cluster_mapping() ;
		map_genes_through_clusters( m_cluster_mapping ) ;

		summarise() ;

		run_tests() ;
	}

	void load_data() {
		StringStringMap pathway_names ;
		StringStringSetMap pathway_members ;
		StringStringSetMap clusters ;
		StringStringMap cluster_descriptions ;
		StringSet universe ;
		StringSet test_genes ;

		assert( options().check( "-u" ) ) ;
		if( options().check( "-u" )) {
			load_list_of_strings(
				options().get< std::string >( "-u" ),
				boost::bind(
					&impl::insert_into< StringSet >,
					_1,
					&universe
				)
			) ;

			get_ui_context().logger()
				<< "I loaded " << universe.size() << " universe genes.  First few are:\n" ;
			StringSet::const_iterator
				i = universe.begin(),
				end_i = universe.end() ;
			for( std::size_t count = 0; count < 5 && i != end_i; ++count, ++i ) {
				get_ui_context().logger() << "  " << *i << "\n" ;
			}
		}

		if( options().check( "-c" )) {
			statfile::BuiltInTypeStatSource::UniquePtr source = statfile::BuiltInTypeStatSource::open(
				options().get< std::string >( "-c" )
			) ;
			load_clusters(
				*source,
				impl::MapSetSetter( clusters ),
				impl::MapSetter( cluster_descriptions )
			) ;
			get_ui_context().logger() << "Loaded " << clusters.size() << " clusters:\n" ;
			foreach( StringStringSetMap::value_type const& elt, clusters ) {
				get_ui_context().logger()
					<< "  " << elt.first << ": " ;
				foreach( StringSet::value_type const& value, elt.second ) {
					get_ui_context().logger() << value << " " ;
				}
				get_ui_context().logger() << "\n" ;
			}
		}

		if( options().check( "-p" )) {
			std::vector< std::string > elts = options().get_values< std::string >( "-p" ) ;
			foreach( std::string const& elt, elts ) {
				statfile::BuiltInTypeStatSource::UniquePtr source = statfile::BuiltInTypeStatSource::open(
					genfile::wildcard::find_files_by_chromosome( elt )
				) ;
				load_pathways_list(
					*source,
					impl::MapSetter( pathway_names ),
					impl::MapSetSetter( pathway_members )
				) ;
			}

			typedef std::pair< std::string, std::set< std::string > > StringStringSet ;
			if( options().check( "-intersect" )) {
				StringSet pathway_universe ;
				foreach( StringStringSet const& entry, pathway_members ) {
					pathway_universe.insert( entry.second.begin(), entry.second.end() ) ;
				}
				get_ui_context().logger() << "Created pathway universe of " << universe.size() << " genes based on all genes in pathways.\n" ;
				std::size_t const N = universe.size() ;
				universe = genfile::utility::intersect( universe, pathway_universe ) ;
				get_ui_context().logger() << "Formed intersection with universe (" << universe.size() << " of " << N << " in original universe.\n" ;

			}

			std::map< std::string, StringSet > universe_pathway_members ;
			foreach( StringStringSet const& entry, pathway_members ) {
				universe_pathway_members[ entry.first ] = genfile::utility::intersect( entry.second, universe ) ;
			}

			get_ui_context().logger()
				<< "I loaded " << pathway_names.size() << " pathways.  First few are:\n" ;
			std::map< std::string, std::string >::const_iterator
				pathway_i = pathway_names.begin(),
				pathway_end = pathway_names.end() ;
			for( std::size_t count = 0; count < 5 && pathway_i != pathway_end; ++count, ++pathway_i ) {
				get_ui_context().logger() << "  " << pathway_i->first << ": " << pathway_i->second
					<< " (" << pathway_members[pathway_i->first].size()
					<< " genes of which "
					<< universe_pathway_members[pathway_i->first].size()
					<<" are in the universe)\n" ;
			}

			pathway_members = universe_pathway_members ;
		}

		if( options().check( "-g" )) {
			load_list_of_strings(
				options().get< std::string >( "-g" ),
				boost::bind(
					impl::insert_into< StringSet >,
					_1,
					&test_genes
				)
			) ;

			get_ui_context().logger()
				<< "I loaded " << test_genes.size() << " test genes " ;
			test_genes = genfile::utility::intersect( test_genes, universe ) ;
			get_ui_context().logger()
				<< "of which " << test_genes.size() << " are in the universe.  First few are:\n" ;
			StringSet::const_iterator
				i = test_genes.begin(),
				end_i = test_genes.end() ;
			for( std::size_t count = 0; count < 5 && i != end_i; ++count, ++i ) {
				get_ui_context().logger() << "  " << *i << "\n" ;
			}
		}

		m_pathway_names = pathway_names ;
		m_pathway_members = pathway_members ;
		m_clusters = clusters ;
		m_cluster_descriptions = cluster_descriptions ;
		m_universe = universe ;
		m_test_genes = test_genes ;
	}

	StringStringMap get_cluster_mapping() {
		get_ui_context().logger() << "Computing gene->cluster mapping...\n" ;
		StringStringMap result ;
		foreach( StringStringSetMap::value_type const& cluster, m_clusters ) {
			foreach( std::string const& regex_string, cluster.second ) {
				boost::regex regex( regex_string ) ;
				StringStringMap const& this_map = get_cluster_mapping( regex, cluster.first ) ;
				result.insert( this_map.begin(), this_map.end() ) ;
			}
		}

		return result ;
	}

	StringStringMap get_cluster_mapping( boost::regex const& regex, std::string const& replacement ) {
		StringStringMap result ;
		get_cluster_mapping( m_universe, regex, replacement, boost::bind( impl::insert_into< StringStringMap >, _1, &result )) ;
		get_cluster_mapping( m_test_genes, regex, replacement, boost::bind( impl::insert_into< StringStringMap >, _1, &result )) ;
		foreach( StringStringSetMap::value_type& value, m_pathway_members ) {
			get_cluster_mapping( value.second, regex, replacement, boost::bind( impl::insert_into< StringStringMap >, _1, &result )) ;
		}
		return result ;
	}

	void get_cluster_mapping(
		StringSet& values,
		boost::regex const& regex,
		std::string const& replacement,
		boost::function< void ( std::pair< std::string, std::string > const& ) > output
	) {
		StringSet::iterator i = values.begin(), end_i = values.end() ;
		for( ; i != end_i; ++i ) {
			std::string value = *i ;
			value = boost::regex_replace( value, regex, replacement ) ;
			if( value != *i ) {
				output( std::make_pair( *i, value ) ) ;
			}
		}
	}

	void map_genes_through_clusters( StringStringMap const& mapping ) {
		get_ui_context().logger() << "Mapping genes through clusters...\n" ;
		std::size_t n = m_universe.size() ;
		m_universe = map_genes_through_clusters( mapping, m_universe ) ;
		get_ui_context().logger() << "Mapped " << n << " genes to " << m_universe.size() << " in universe.\n" ;
		n = m_test_genes.size() ;
		m_test_genes = map_genes_through_clusters( mapping, m_test_genes ) ;
		get_ui_context().logger() << "Mapped " << n << " genes to " << m_test_genes.size() << " in test genes.\n" ;
		foreach( StringStringSetMap::value_type& value, m_pathway_members ) {
			value.second = map_genes_through_clusters( mapping, value.second ) ;
		}
	}

	StringSet map_genes_through_clusters( StringStringMap const& mapping, StringSet const& genes ) {
		StringSet result ;
		foreach( StringSet::value_type const& value, genes ) {
			StringStringMap::const_iterator where = mapping.find( value ) ;
			if( where != mapping.end() ) {
				result.insert( where->second ) ;
			} else {
				result.insert( value ) ;
			}
        }
		return result ;
	}

	void summarise() {
		get_ui_context().logger() << "\n-------------------------\n\n" ;
		if( m_clusters.size() > 0 ) {
			get_ui_context().logger() << std::setw( 12 ) << "Cluster mapping:\n" ;
            StringStringSetMap backMap ;
			foreach( StringStringMap::value_type const& key_value, m_cluster_mapping ) {
                backMap[ key_value.second ].insert( key_value.first ) ;
			}
            foreach( StringStringSetMap::value_type const& k, backMap ) {
                get_ui_context().logger() << std::setw( 12 ) << k.first << ":" ;
				std::size_t count = 0 ;
				for( StringSet::const_iterator i = k.second.begin(); i != k.second.end(); ++i, ++count ) {
                    get_ui_context().logger() << " " << *i ;
					if( count == 10 ) {
						get_ui_context().logger() << " (+ " << k.second.size() - 10 << " others)..." ;
						break ;
					}
                }
                get_ui_context().logger() << "\n" ;
            }
		}
#if 0
		if( m_clusters.size() > 0 ) {
			get_ui_context().logger() << std::setw( 12 ) << "Cluster mapping:\n" ;
			foreach( StringStringMap::value_type const& key_value, m_cluster_mapping ) {
				get_ui_context().logger() << std::setw( 12 ) << key_value.first << ": "  << key_value.second << "\n" ;
			}
		}
#endif
		get_ui_context().logger() << std::setw( 12 ) << "Test genes:" << "  " << m_test_genes.size() << " genes\n" ;
		get_ui_context().logger() << std::setw( 12 ) << "Pathways:" << "  " << m_pathway_names.size() << " pathways\n" ;
		get_ui_context().logger() << std::setw( 12 ) << "Universe:" << "  " << m_universe.size() << " genes\n" ;
	}

	void run_tests() {
		// table is
		//                 in pathway | not in pathway |
		//     test gene |     a      |       b        |
		// Not test gene |     c      |       d        |
		//
		Eigen::Matrix2d table ;

		using std::setw ;
		std::string const tab = "\t" ;
		std::cout
		//get_ui_context().logger()
			<< "analysis" << tab
			<< "pathway id" << tab
			<< "pathway name" << tab
			<< "hits in pathway" << tab
			<< "hits not in pathway" << tab
			<< "nonhits in pathway" << tab
			<< "nonhits not in pathway" << tab
			<< "total in pathway" << tab
			<< "total genes" << tab
			<< "sample odds ratio" << tab
			<< "fishers exact test p value" << tab
			<< "ids.of.hits.in.pathway"
			<< "\n" ;

		std::string const analysis = options().get< std::string >( "-analysis-name" ) ;
		double const pvalue_threshhold = options().get< double >( "-P-value" ) ;
		std::ostringstream hit_genes_in_pathway ;
		foreach( StringStringSetMap::value_type const& pathway, m_pathway_members ) {
			hit_genes_in_pathway.str( "" ) ;
			table = Eigen::Matrix2d::Zero() ;

			foreach( std::string const& value, m_universe ) {
				int i = 0, j = 0 ;
				if( impl::in( value, m_test_genes )) {
					i = 0 ;
				} else {
					i = 1 ;
				}

				if( impl::in( value, pathway.second ) ) {
					j = 0 ;
				} else {
					j = 1 ;
				}

				++table( i, j ) ;

				if( i == 0 && j == 0 ) {
					if( hit_genes_in_pathway.str().size() == 0 ) {
						hit_genes_in_pathway << value ;
					}
					else {
						hit_genes_in_pathway << "," << value ;
					}
				}
			}

			//get_ui_context().logger()
			std::cout
				<< analysis << tab
				<< pathway.first << tab
				<< "\"" << m_pathway_names[ pathway.first ] << "\"" << tab
				<< table( 0, 0 ) << tab
				<< table( 0, 1 ) << tab
				<< table( 1, 0 ) << tab
				<< table( 1, 1 ) << tab
				<< table.col( 0 ).sum() << tab
				<< table.sum() << tab ;
			try {
				FishersExactTest test( table ) ;
				//get_ui_context().logger()
				std::cout
					<< test.get_OR() << tab << test.get_pvalue() ;
				if( test.get_pvalue() <= pvalue_threshhold ) {
					std::cout << tab << hit_genes_in_pathway.str() ;
				} else {
					std::cout << tab << "NA" ;
				}
			}
			catch( std::exception const& ) {
				// get_ui_context().logger()
				std::cout
					<< "NA\tNA\t" ;
			}
			std::cout << "\n" ;
		}
	}

private:
	StringStringMap m_pathway_names ;
	StringStringSetMap m_pathway_members ;
	StringStringSetMap m_clusters ;
	StringStringMap m_cluster_descriptions ;
	StringStringMap m_cluster_mapping ;
	StringSet m_universe ;
	StringSet m_test_genes ;
} ;

int main( int argc, char **argv ) {
	try {
		OverrepApplication app( argc, argv ) ;
		app.process() ;
	}
	catch( appcontext::HaltProgramWithReturnCode const& e ) {
		return e.return_code() ;
	}
	return 0 ;
}