#ifndef S3D_CV_UTILITIES_CV_H
#define S3D_CV_UTILITIES_CV_H

#include "s3d/image/image.h"
#include "s3d/utilities/random_color_generator.h"
#include "s3d/video/video_frame.h"
#include "s3d/video/video_types.h"
#include "s3d/utilities/eigen.h"

#include <opencv/cxeigen.hpp>
#include <opencv2/opencv.hpp>

#include <cstdint>

#include <gsl/gsl>

#include <cassert>
#include <cmath>
#include <tuple>

namespace s3d {

inline Image<uint8_t> cv2image(cv::Mat cvImg) {
  Image<uint8_t> img(Size(cvImg.cols, cvImg.rows));
  cv::cv2eigen(cvImg, img);
  return img;
}

inline cv::Mat image2cv(const Image<uint8_t>& img) {
  cv::Mat cvImg;
  cv::eigen2cv(img, cvImg);
  return cvImg;
}

inline int nbChannelsToMatType(int nbChannels) {
  int imgType = CV_8U;
  switch (nbChannels) {
    case 1:
      imgType = CV_8U;
      break;
    case 2:
      imgType = CV_8UC2;
      break;
    case 3:
      imgType = CV_8UC3;
      break;
    case 4:
      imgType = CV_8UC4;
      break;
    default:
      break;
  }
  return imgType;
}

inline cv::Mat dataToMat(Size size, VideoPixelFormat pixelFormat, const gsl::span<const uint8_t>& data) {
  auto nbChannels = static_cast<int>(VideoFrame::NumBytesPerPixel(pixelFormat));
  return cv::Mat(size.getHeight(),
                 size.getWidth(),
                 nbChannelsToMatType(nbChannels),
                 const_cast<uchar*>(data.data()));
}
// does not copy data, to copy use resizeMat(...).clone();
inline void resizeMat(gsl::not_null<cv::Mat*> mat, float colsRatio, float rowsRatio) {
  float cols = mat->cols;
  float rows = mat->rows;
  // todo: use linear if upsampling, inter_area is good for down sampling
  cv::resize(*mat, *mat, cv::Size2f(cols * colsRatio, rows * rowsRatio), cv::INTER_AREA);
}

inline void resizeMat(gsl::not_null<cv::Mat*> mat, float ratio) {
  resizeMat(mat, ratio, ratio);
}

inline void displayInNewWindow(const std::string& name, cv::InputArray src) {
  cv::namedWindow(name, cv::WINDOW_AUTOSIZE);
  cv::imshow(name, src);
}

template <class T>
void displayMatches(const std::string& title,
                    const cv::Mat& left,
                    const cv::Mat& right,
                    const std::vector<Eigen::Matrix<T, 2, 1>>& featuresLeft,
                    const std::vector<Eigen::Matrix<T, 2, 1>>& featuresRight) {
  cv::Mat leftRight(left.rows, left.cols * 2, left.type());
  cv::hconcat(left, right, leftRight);

  // convert to color
  if (left.depth() == 1) {
    cv::cvtColor(leftRight, leftRight, cv::COLOR_GRAY2BGR);
  }

  // for random color
  RandomColorGenerator<cv::Scalar> colorGenerator;

  for (size_t i = 0; i < featuresLeft.size(); ++i) {
    // random color
    cv::Scalar color = colorGenerator.randomColor();

    // discretize features
    const Eigen::Vector2i featureLeft = featuresLeft[i].template cast<int>();
    const Eigen::Vector2i featureRight =
        featuresRight[i].template cast<int>() + Eigen::Vector2i(left.cols, 0.0f);

    const cv::Point ptLeft(featureLeft.x(), featureLeft.y());
    const cv::Point ptRight(featureRight.x(), featureRight.y());

    cv::line(leftRight, ptLeft, ptRight, color, 1, CV_AA);
    cv::circle(leftRight, ptLeft, 3, color, 1, CV_AA);
    cv::circle(leftRight, ptRight, 3, color, 1, CV_AA);
  }

  displayInNewWindow(title, leftRight);
}

template <class T>
std::vector<cv::Point_<T>> eigenPointsToCV(std::vector<Eigen::Matrix<T, 2, 1>> points) {
  std::vector<cv::Point_<T>> pointsCV;
  for (auto point : points) {
    pointsCV.emplace_back(point.x(), point.y());
  }
  return pointsCV;
}

template <class T, int N, int M>
cv::Matx<T, N, M> eigenMatToCV(Eigen::Matrix<T, N, M> mat) {
  cv::Matx<T, 3, 3> matCV;
  cv::eigen2cv(mat, matCV);
  return matCV;
};

template <typename T>
static float distancePointLine(const cv::Point_<T> point, const cv::Vec<T, 3>& line) {
  // Line is given as a*x + b*y + c = 0
  return std::abs(line(0) * point.x + line(1) * point.y + line(2)) /
         std::sqrt(line(0) * line(0) + line(1) * line(1));
}

/**
 * \brief Compute and draw the epipolar lines in two images
 *      associated to each other by a fundamental matrix
 *
 * \param title     Title of the window to display
 * \param F         Fundamental matrix
 * \param img1      First image
 * \param img2      Second image
 * \param points1   Set of points in the first image
 * \param points2   Set of points in the second image matching to the first set
 * \param inlierDistance      Points with a high distance to the epipolar lines are
 *                not displayed. If it is negative, all points are displayed
 **/
template <typename T1, typename T2>
static std::tuple<cv::Mat, cv::Mat> drawEpipolarLines(const cv::Matx<T1, 3, 3> F,
                                                      const cv::Mat& img1,
                                                      const cv::Mat& img2,
                                                      const std::vector<cv::Point_<T2>>& points1,
                                                      const std::vector<cv::Point_<T2>>& points2,
                                                      const float inlierDistance = -1) {
  assert(img1.size() == img2.size() && img1.type() == img2.type());
  assert(points1.size() == points2.size());
  assert(points1.size() > 0);

  cv::Mat outImgLeft(img1.rows, img1.cols, CV_8UC3);
  cv::Mat outImgRight(img1.rows, img1.cols, CV_8UC3);

  // for color drawing
  if (img1.type() == CV_8U) {
    cv::cvtColor(img1, outImgLeft, CV_GRAY2BGR);
    cv::cvtColor(img2, outImgRight, CV_GRAY2BGR);
  } else {
    img1.copyTo(outImgLeft);
    img2.copyTo(outImgRight);
  }

  // compute epilines for image 1 and 2
  std::vector<cv::Vec<T2, 3>> epilines1, epilines2;
  cv::computeCorrespondEpilines(points1, 1, F, epilines1);
  cv::computeCorrespondEpilines(points2, 2, F, epilines2);

  assert(points1.size() == points2.size() && points2.size() == epilines1.size() &&
         epilines1.size() == epilines2.size());

  s3d::RandomColorGenerator<cv::Scalar> colorGenerator;
  for (size_t i = 0; i < points1.size(); i++) {
    if (inlierDistance > 0) {
      if (distancePointLine(points1[i], epilines2[i]) > inlierDistance ||
          distancePointLine(points2[i], epilines1[i]) > inlierDistance) {
        // The point match is no inlier
        continue;
      }
    }

    // epipolar lines of the 1st point set are drawn in the 2nd image and vice-versa
    cv::Scalar color = colorGenerator.randomColor();

    cv::line(
        outImgRight,
        cv::Point(0, -epilines1[i][2] / epilines1[i][1]),
        cv::Point(img1.cols, -(epilines1[i][2] + epilines1[i][0] * img1.cols) / epilines1[i][1]),
        color,
        1,
        CV_AA);
    cv::circle(outImgLeft, points1[i], 3, color, -1, CV_AA);

    cv::line(
        outImgLeft,
        cv::Point(0, -epilines2[i][2] / epilines2[i][1]),
        cv::Point(img2.cols, -(epilines2[i][2] + epilines2[i][0] * img2.cols) / epilines2[i][1]),
        color,
        1,
        CV_AA);
    cv::circle(outImgRight, points2[i], 3, color, -1, CV_AA);
  }

  return std::tuple<cv::Mat, cv::Mat>(outImgLeft, outImgRight);
}

/**
 * \brief Compute and draw the epipolar lines in two images
 *      associated to each other by a fundamental matrix
 *
 * \param title     Title of the window to display
 * \param F         Fundamental matrix
 * \param img1      First image
 * \param img2      Second image
 * \param points1   Set of points in the first image
 * \param points2   Set of points in the second image matching to the first set
 **/
template <typename T1, typename T2>
static void displayEpipolarLines(const std::string& title,
                                 const cv::Matx<T1, 3, 3> F,
                                 const cv::Mat& img1,
                                 const cv::Mat& img2,
                                 const std::vector<cv::Point_<T2>>& points1,
                                 const std::vector<cv::Point_<T2>>& points2) {
  assert(img1.size() == img2.size() && img1.type() == img2.type());

  cv::Mat imgLeftEpilines, imgRightEpilines;
  std::tie(imgLeftEpilines, imgRightEpilines) =
      s3d::drawEpipolarLines(F, img1, img2, points1, points2);

  // horizontal concat
  cv::Mat outImg(img1.rows, img1.cols * 2, CV_8UC3);
  cv::hconcat(imgLeftEpilines, imgRightEpilines, outImg);

  cv::imshow(title, outImg);
  cv::waitKey(1);
}

}  // namespace s3d

#endif  // S3D_CV_UTILITIES_CV_H


/* Copyright 2014-2018 The PySCF Developers. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

#include<stdio.h>
#include<stdlib.h>
#include <omp.h>
//#include <cblas.h>

/*
!
!
!Compute Y += A*X for CSR matrix A and dense vectors X,Y
! From scipy/sparse/sparsetools/csr.h
!
!
! Input Arguments:
!   I  n_row         - number of rows in A
!   I  n_col         - number of columns in A
!   I  Ap[n_row+1]   - row pointer
!   I  Aj[nnz(A)]    - column indices
!   T  Ax[nnz(A)]    - nonzeros
!   T  Xx[n_col]     - input vector
!
! Output Arguments:
!  T  Yx[n_row]     - output vector
!
! Note:
!   Output array Yx must be preallocated
!
!   Complexity: Linear.  Specifically O(nnz(A) + n_row)
*/
void scsr_matvec(int nrow, int ncol, int nnz, int *Ap, int *Aj,
    float *Ax, float *Xx, float *Yx)
{

  int i, jj;
  float sum = 0.0;

  # pragma omp parallel \
  shared (nrow, Yx, Ap, Ax, Xx, Aj) \
  private (i, jj, sum)
  {
    #pragma omp for
    for(i = 0; i < nrow; i++){
      sum = Yx[i];
      for(jj = Ap[i]; jj < Ap[i+1]; jj++){
        sum += Ax[jj] * Xx[Aj[jj]];
      }
      Yx[i] = sum;
    }
  }
}

void dcsr_matvec(int nrow, int ncol, int nnz, int *Ap, int *Aj,
    double *Ax, double *Xx, double *Yx)
{

  int i, jj;
  double sum = 0.0;

  # pragma omp parallel \
  shared (nrow, Yx, Ap, Ax, Xx, Aj) \
  private (i, jj, sum)
  {
    #pragma omp for
    for(i = 0; i < nrow; i++){
      sum = Yx[i];
      for(jj = Ap[i]; jj < Ap[i+1]; jj++){
        sum += Ax[jj] * Xx[Aj[jj]];
      }
      Yx[i] = sum;
    }
  }
}

/*
 * Compute Y += A*X for CSC matrix A and dense vectors X,Y
 * From scipy/sparse/sparsetools/csc.h
 *
 *
 * Input Arguments:
 *   I  n_row         - number of rows in A
 *   I  n_col         - number of columns in A
 *   I  Ap[n_row+1]   - column pointer
 *   I  Ai[nnz(A)]    - row indices
 *   T  Ax[n_col]     - nonzeros
 *   T  Xx[n_col]     - input vector
 *
 * Output Arguments:
 *   T  Yx[n_row]     - output vector
 *
 * Note:
 *   Output array Yx must be preallocated
 *
 *   Complexity: Linear.  Specifically O(nnz(A) + n_col)
 *
 */
void scsc_matvec(int n_row, int n_col, int nnz,
            int *Ap, int *Ai, float *Ax, float *Xx, float *Yx)
{
    int col_start, col_end, j, ii, i;

    for( j = 0; j < n_col; j++){
        col_start = Ap[j];
        col_end   = Ap[j+1];

        for( ii = col_start; ii < col_end; ii++){
            i    = Ai[ii];
            Yx[i] += Ax[ii] * Xx[j];
        }
    }
}

void dcsc_matvec(int n_row, int n_col, int nnz,
            int *Ap, int *Ai, double *Ax, double *Xx, double *Yx)
{
    int col_start, col_end, j, ii, i;

    for( j = 0; j < n_col; j++){
        col_start = Ap[j];
        col_end   = Ap[j+1];

        for( ii = col_start; ii < col_end; ii++){
            i    = Ai[ii];
            Yx[i] += Ax[ii] * Xx[j];
        }
    }
}

/*
 * Compute Y += A*X for CSC matrix A and dense block vectors X,Y
 * From scipy/sparse/sparsetools/csc.h
 *
 *
 * Input Arguments:
 *   I  n_row            - number of rows in A
 *   I  n_col            - number of columns in A
 *   I  n_vecs           - number of column vectors in X and Y
 *   I  Ap[n_row+1]      - row pointer
 *   I  Aj[nnz(A)]       - column indices
 *   T  Ax[nnz(A)]       - nonzeros
 *   T  Xx[n_col,n_vecs] - input vector
 *
 * Output Arguments:
 *   T  Yx[n_row,n_vecs] - output vector
 *
 * Note:
 *   Output array Yx must be preallocated
 *
*/
void scsc_matvecs(int n_row, int n_col, int n_vecs,
      int *Ap, int *Ai, float *Ax, float *Xx, float *Yx)
{
  int i, j, ii;
  printf("dcsc_matvecs not working in m_sparsetools.c\n");
  exit(1);
  /*
  # pragma omp parallel \
  shared (n_row, n_col, n_vecs, Ap, Ai, Ax, Xx, Yx) \
  private (i, ii, j)
  {
    #pragma omp for
    for( j = 0; j < n_col; j++){
      for( ii = Ap[j]; ii < Ap[j+1]; ii++){
        i = Ai[ii];
        //axpy(n_vecs, Ax[ii], Xx + (int)n_vecs * j, Yx + (int)n_vecs * i);
        cblas_saxpy (n_vecs, Ax[ii], &Xx[n_vecs*j], 1, &Yx[n_vecs*i], 1);
      }
    }
  //}
    */
}

void dcsc_matvecs(int n_row, int n_col, int n_vecs,
      int *Ap, int *Ai, double *Ax, double *Xx, double *Yx)
{
  int i, j, ii;
  printf("dcsc_matvecs not working in m_sparsetools.c\n");
  exit(1);
  /*
  # pragma omp parallel \
  shared (n_row, n_col, n_vecs, Ap, Ai, Ax, Xx, Yx) \
  private (i, ii, j)
  {
    #pragma omp for
    for( j = 0; j < n_col; j++){
      for( ii = Ap[j]; ii < Ap[j+1]; ii++){
        i = Ai[ii];
        //axpy(n_vecs, Ax[ii], Xx + (int)n_vecs * j, Yx + (int)n_vecs * i);
        cblas_daxpy (n_vecs, Ax[ii], &Xx[n_vecs*j], 1, &Yx[n_vecs*i], 1);
      }
    }
  //}
  */
}


/*System includes*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

/*GSL includes*/
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_sf.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_statistics_double.h>
#include <gsl/gsl_fft_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_multimin.h>
#include <pthread.h>

/*User includes*/

#include "Subsample.h"

static char *usage[] = {"Subsample - Subsamples an emprical rarefraction curve by resampling\n",
                        "Required parameters:\n",
			"   -in   filename      parameter file  \n",
			"   -i    integer       sample to subsample from\n",
			"   -r    integer       number of samples to generate\n",
			"   -n    integer       new sample size\n",
                        "Optional:\n",
			"   -seed long          seed random number generator\n",
			"   -v                  verbose\n"};

static int  nLines   = 9;

static int  verbose  = FALSE;

void setCP(double* adP, double *adC, int *anN, int nS, int nL)
{
  int i = 0;

  for(i = 0; i < nS; i++){
    adP[i] = ((double) anN[i])/((double) nL);
  }

  adC[i] = 0;
  for(i = 1; i <= nS; i++){
    adC[i] = adP[i - 1] + adC[i - 1];
  }
}

int sampleSpecies(gsl_rng *ptGSLRNG, double *adC, int nS)
{
  double dRand = gsl_rng_uniform(ptGSLRNG);
  int    nRet  = ((double) nS)*dRand;

  while(dRand <= adC[nRet]) nRet--;

  while(dRand > adC[nRet + 1]) nRet++;

  return nRet;
}

void writeSample(t_Params *ptParams, int nSample, int nR, int nS, int* anF)
{
  FILE *ofp = NULL;
  int i   = 0;
  char szFileName[MAX_LINE_LENGTH];

  sprintf(szFileName, "%s_%d_%d.dat",ptParams->szOutFileStub,nR, nSample);

  ofp = fopen(szFileName, "w");

  for(i = 0; i < nS; i++){
    fprintf(ofp,"%d %d\n",i, anF[i]);
  }

  fclose(ofp);
}

int main(int argc, char* argv[])
{
  int  i = 0, j = 0, r = 0;
  int  nCount = 0, nRS = 0, nD = 0;
  t_Params tParams;
  int      nS = 0, nL = 0;
  double   *adP = NULL;
  double   *adC = NULL;
  int      *anN = NULL;
  int      **aanF = NULL;
  gsl_rng            *ptGSLRNG = NULL;
  const gsl_rng_type *ptGSLRNGType = NULL;

  gsl_rng_env_setup();

  gsl_set_error_handler_off();

  /*get command line params*/
  getCommandLineParams(&tParams, argc, argv);

  ptGSLRNGType = gsl_rng_default;
  ptGSLRNG     = gsl_rng_alloc(ptGSLRNGType);

  gsl_rng_set(ptGSLRNG, tParams.lSeed);

  /*read in abundance distribution*/
  readAbundanceData(tParams.nI, tParams.szInputFile,&anN,&nS);

  for(i = 0; i < nS; i++){
    nL += anN[i];
  }

  adP = (double *) malloc(sizeof(double)*nS);
  adC = (double *) malloc(sizeof(double)*(nS + 1));
  aanF = (int    **) malloc(sizeof(int*)*nS);
  for(i = 0; i < nS; i++){
    aanF[i] = (int *) malloc(tParams.nR*sizeof(int));
  }

  setCP(adP, adC, anN, nS, nL);

  while(r < tParams.nR){

    for(i = 0; i < nS; i++){
      aanF[i][r] = 0;
    }

    for(i = 0; i < tParams.nN; i++){
      nD = sampleSpecies(ptGSLRNG, adC, nS);

      aanF[nD][r]++;
    }/*sample*/

    r++;
  }
  gsl_rng_free(ptGSLRNG);

  printf("OTU,");
  for(r = 0; r < tParams.nR - 1; r++){
    printf("r%d,",r);
  }
  printf("r%d\n",r);

  for(i = 0; i < nS; i++){
    printf("C%d,",i);
    for(r = 0; r < tParams.nR - 1; r++){
      printf("%d,",aanF[i][r]);
    }
    printf("%d\n",aanF[i][r]);
  }

  for(i = 0; i < nS; i++){
    free(aanF[i]);
  }
  free(aanF);
  free(anN);
  free(adC);
  free(adP);

  exit(EXIT_SUCCESS);
}

void writeUsage(FILE* ofp)
{
  int i = 0;
  char *line;

  for(i = 0; i < nLines; i++){
    line = usage[i];
    fputs(line,ofp);
  }
}

char *extractParameter(int argc, char **argv, char *param,int when)
{
  int i = 0;

  while((i < argc) && (strcmp(param,argv[i]))){
    i++;
  }

  if(i < argc - 1){
    return(argv[i + 1]);
  }

  if((i == argc - 1) && (when == OPTION)){
    return "";
  }

  if(when == ALWAYS){
    fprintf(stdout,"Can't find asked option %s\n",param);
  }

  return (char *) NULL;
}

void getCommandLineParams(t_Params *ptParams,int argc,char *argv[])
{
  char *szTemp = NULL;
  char *cError = NULL;

  /*get parameter file name*/
  ptParams->szInputFile  = extractParameter(argc,argv, INPUT_FILE,ALWAYS);
  if(ptParams->szInputFile == NULL)
    goto error;

  /*get out file stub*/
  //ptParams->szOutFileStub  = extractParameter(argc,argv,OUT_FILE_STUB,ALWAYS);
  //if(ptParams->szOutFileStub == NULL)
    //goto error;

  szTemp  = extractParameter(argc,argv,SAMPLE,OPTION);
  if(szTemp != NULL){
    ptParams->nI = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->nI = 1;
  }

  szTemp  = extractParameter(argc,argv,N_SAMPLES,OPTION);
  if(szTemp != NULL){
    ptParams->nR = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->nR = 10;
  }

  szTemp  = extractParameter(argc,argv,N_SIZE,OPTION);
  if(szTemp != NULL){
    ptParams->nN = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->nN = 1000;
  }

  /*get out file stub*/
  szTemp  = extractParameter(argc,argv,SEED,OPTION);
  if(szTemp != NULL){
    ptParams->lSeed = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->lSeed = 0;
  }

  /*verbosity*/
  szTemp = extractParameter(argc, argv, VERBOSE, OPTION);
  if(szTemp != NULL){
    verbose = TRUE;
  }

  return;

 error:
  writeUsage(stdout);
  exit(EXIT_FAILURE);
}

void readAbundanceData(int nI, const char *szFile, int **panN, int *pnS)
{
  int  *anN = NULL;
  int  i = 0, j = 0, nS = 0;
  char szLine[MAX_LINE_LENGTH];
  FILE* ifp = NULL;

  ifp = fopen(szFile, "r");

  if(ifp){
    char* szTok   = NULL;
    char* pcError = NULL;

    fgets(szLine, MAX_LINE_LENGTH, ifp);

    while(fgets(szLine, MAX_LINE_LENGTH, ifp) != NULL){
    	nS++;
    }
    fclose(ifp);

    ifp = fopen(szFile, "r");
    fgets(szLine, MAX_LINE_LENGTH, ifp);
    anN = (int *) malloc(nS*sizeof(int));

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

      fgets(szLine, MAX_LINE_LENGTH, ifp);

      szTok = strtok(szLine, DELIM);

      for(j = 0; j < nI; j++){
 	szTok = strtok(NULL, DELIM);
      }
      anN[i] = strtol(szTok,&pcError,10);

      if(*pcError != '\0'){
	goto formatError;
      }
    }
  }
  else{
    fprintf(stderr, "Failed to open abundance data file %s aborting\n", szFile);
    fflush(stderr);
    exit(EXIT_FAILURE);
  }

  (*pnS) = nS;
  (*panN) = anN;
  return;

 formatError:
  fprintf(stderr, "Incorrectly formatted abundance data file\n");
  fflush(stderr);
  exit(EXIT_FAILURE);
}

int compare_doubles(const void* a, const void* b)
{
  double* arg1 = (double *) a;
  double* arg2 = (double *) b;
  if( *arg1 < *arg2 ) return -1;
  else if( *arg1 == *arg2 ) return 0;
  else return 1;
}


/*
 *  aplowly.h
 *  lowly_project
 *
 *  Created by Karim Benabed on 11/03/09.
 *  Copyright 2009 Institut d'Astrophysique de Paris. All rights reserved.
 *
 */

#include "lowly_common.h"

#include <gsl/gsl_vector.h>
#include <gsl/gsl_vector_int.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_gamma.h>

#ifndef __APLOWLY__
#define __APLOWLY__

typedef struct {
  double *data;
  double *alpha,*beta,*wl,*C,*gl;
  double Nl,logdets2;
  int is_zero;
  size_t ndim;
  gsl_matrix_view  C_view_container;
  gsl_matrix  *C_view;
  gsl_vector_view  gl_view_container;
  gsl_vector *gl_view;

} approx_lowly;

approx_lowly* approx_lowly_init(size_t ndim,int* ell, int lmax,double *clhat, double *fsky, double *wl, double *V,double noisevar, long nside, error **err);
void free_approx_lowly(approx_lowly **self);
double approx_lowly_log_pdf(void *self, double *Cl, error **err);
double approx_zero_lowly_log_pdf(void* pelf,double* pars, error **err);

typedef struct {
  double *data;
  double *al,*bl,*Cm,*gl,*nl;
  int *ell;
  double logdets2;
  int isdiag;
  int nell;
} tease;

typedef struct {
  double *data;
  double *cl,*vl,*nl,*Cm,*gl;
  int *ell;
  double logdets2;
  int isdiag;
  int nell;
} gausslkl;

tease* tease_init(size_t nell, int* ell,double *al, double *bl, double *nl, int nlcst, double *Cm, int Cmisdiag, error **err);
void tease_free(void **ping);
double tease_log_pdf(void* ing, double* pars, error **err);

gausslkl* gausslkl_init(size_t nell, int* ell,double *al, double *bl, double *nl, int nlcst, double *Cm, int Cmisdiag, error **err);
void gausslkl_free(void **ping);
double gausslkl_log_pdf(void* ing, double* pars, error **err);

typedef struct {
  double *data;
  double *ldlh,*sig2,*wl;
  double Nl;
  size_t ndim;
} log_normal;

log_normal* log_normal_init(size_t ndim,int* ell, double *clhat, double *fsky, double *wl, double noisevar, long nside, error **err);
void free_log_normal(log_normal **self);
double log_normal_log_pdf(void *self, double *Cl, error **err);

typedef struct {
  int np;
  int *nb;
  int *obe;
  int *obv;
  double *be;
  double *bv;
} binlkl;

binlkl* init_binlkl(int np, int* nb, double* be, double* bv, error **err);
double binlkl_lkl(void *vbon, double *pars, error **err);
void free_binlkl(void** pvbon);

#define ZEROLKL -1e30

#define lowly_no_correlation -20 + clowly_base

#endif


/*
/
/ adkGSL.h
/
/ homebrewed addons to gsl */

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_sort_vector.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <math.h>
#include <assert.h>

int gsl_vector_includes(gsl_vector *aVec, double aValue);
double gsl_vector_sum(gsl_vector *aVec, int aVecSize);
double gsl_matrix_row_sum(gsl_matrix *mat, int row, int rowSize);
double gsl_matrix_row_max(gsl_matrix *mat, int iRow);

double log_sum(gsl_vector *vec);
double  *gsl_matrix_2_lapack(gsl_matrix *m);
double gsl_vector_dot_product(gsl_vector *vec1, gsl_vector *vec2);
void gsl_vector_outer_product(gsl_vector *vec1, gsl_vector *vec2, gsl_matrix *result);

gsl_matrix *gsl_matrix_power(gsl_matrix *A, int k);
gsl_matrix *gsl_matrix_power_logs(gsl_matrix *A, int k);
void gsl_matrix_lower_tri_copy(gsl_matrix *src, gsl_matrix *dest);
void gsl_matrix_upper_tri_copy(gsl_matrix *src, gsl_matrix *dest);
void gsl_matrix_lower_tri(gsl_matrix *x);
void gsl_matrix_upper_tri(gsl_matrix *x);
double gsl_matrix_trace(gsl_matrix *x);
void gsl_matrix_covariance(gsl_matrix *data, gsl_matrix *cov);
void gsl_matrix_bootstrap(gsl_matrix *orig, gsl_matrix *boot, gsl_rng *rng);

void gsl_matrix_prettyPrint(gsl_matrix *m);

void fillMatrixFromArray(double *numbers, gsl_matrix *dest, int nrow, int ncol);
void fillMatrixFromVector(gsl_vector *numbers, gsl_matrix *dest, int nrow, int ncol);
void fillArrayFromMatrix(gsl_matrix *src, gsl_vector *dest);

void fillCholArrayFromMatrix(gsl_matrix *src, gsl_vector *dest);
void fillMatrixFromCholArray(double *numbers, gsl_matrix *dest, int nrow, int ncol);

int gsl_matrix_invert_lapack(gsl_matrix *m);

void gsl_matrix_prettyPrint(gsl_matrix *m);
double gsl_matrix_sum(gsl_matrix *mat);

//lapack wrappers
//int dgeev(char jobvl, char jobvr, int n,  double *a, int lda, double *wr, double *wi, double *vl,
//	  int ldvl,double *vr, int ldvr, double *work, int lwork);
//int dhseqr(char job, char compz, int n, int ilo, int ihi,double *h, int ldh, double *wr, double *wi, double *z, int ldz, double *work, int lwork);


#include <getopt.h>
#include <stdio.h>
#include <list>
#include <gsl/gsl_rng.h>
//#include "utils.h"
#include "data.h"
#include "model.h"

// helper function to write out per-user info
void log_user(FILE* file, Data *data, int user, int heldout, double rmse, double mae,
    double rank, int first, double crr, double ncrr, double ndcg, bool stats);

void log_item(FILE* file, Data *data, int item, int heldout, double rmse, double mae,
    double rank, int first, double crr, double ncrr, double ndcg, bool stats);

// helper function to sort predictions properly
bool prediction_compare(const pair<double,int>& itemA,
    const pair<double, int>& itemB);

// take a prediction function as an argument
void eval(Model* model, double (Model::*prediction)(int,int), string outdir, Data* data, bool stats, unsigned long int seed, bool verbose, string label, bool write_rankings, bool mapped_ids);