#include "DDace.h"
#include "DDaceSampler.h"
#include "DDaceRandomSampler.h"
#include "DDaceLHSampler.h"
#include "Array.inl"
#include "NormalDistribution.h"
#include "UniformDistribution.h"
#include "Distribution.h"
#include <cmath>
#include <newmat.h>

int ddaceGenSamples(const int nDim, const int nSamples,
		const Array<double>& xlower, const Array<double>& xupper, 
		Array<DDaceSamplePoint>& samplePts);
///
// OPT++ to DDace interface
// Generate nSamples Latin Hypercube samples from a uniform distribution
// between the values xlower and xupper

int optGenSamples(const int nDim, const int nSamples,
		const ColumnVector& opt_lower, const ColumnVector& opt_upper, 
		Matrix& samplePts)
  //
  // int nDim                        dimension of sample points
  // int nSamples                    number of sample points to generate
  // ColumnVector lower(nDim)        lower range for sample points
  // ColumnVector upper(ndim)        upper range for sample points
  // Matrix samplePts(ndim,nSamples) Matrix of sample points generated by DDace
  //
  //
  //
  //
{

  int argc = 0;
  void** argv;
  PMachine::init(argc, argv);

  int i, j, ierr;
  Array<double> xlower, xupper;
  Array<DDaceSamplePoint> ddaceSamplePts;
  DDaceSamplePoint sample;

  xlower.resize(nDim);
  xupper.resize(nDim);
  ddaceSamplePts.resize(nSamples);

  for (i=0; i < nDim; i++) {
    xlower[i]   = opt_lower(i+1);
    xupper[i]   = opt_upper(i+1);
  }

  //
  // Call function to generate samples
  //
  ierr = ddaceGenSamples(nDim, nSamples, xlower, xupper, ddaceSamplePts);

  // Store sample points in matrix form
  // Each column of the matrix contains 1 DDace sample point

  for (i=0; i < nSamples; i++) {
    sample = ddaceSamplePts[i];
    for (j=0; j < nDim; j++) {
      samplePts(j+1,i+1) = sample[j];
    }
  } 

  return(ierr);
}


///
// Generate nSamples Latin Hypercube samples from a uniform distribution
// between the values xlower and xupper
// Pure DDace version

int ddaceGenSamples(const int nDim, const int nSamples, 
		const Array<double>& xlower, const Array<double>& xupper, 
		Array<DDaceSamplePoint>& samplePts)
{
  int i;
  
  // 1. Create sampler from Uniform dist with 1 replication and no 
  //    random variation around the sample point generated
  // 2. Create DDace object using sampler and store the sample points
  //    in the output array samplePts

  Array<Distribution> dist;
  for (i=0; i < nDim; i++) 
    dist.append(UniformDistribution(xlower[i], xupper[i]));

  DDaceSampler sampler = DDaceLHSampler(nSamples, 1, false, dist);

  DDace ddaceObj(sampler);
  int isamples = 0;
  while(ddaceObj.getNextSample(samplePts[isamples]) && isamples < nSamples-1){
    isamples++;
  }
  
  // Write all the sample points out to the archive file in case
  // the user would like to reuse/store them
  
  ddaceObj.setArchiveName("optDDace.xml");
  ddaceObj.writeToArchive();

  // Check to make sure we generated nSamples points

  PMachine::finalize();

  if (isamples == nSamples-1) {
    return(0);
  }
  else {
    cerr << "ddaceGenSamples: error in generating sample points" << endl;
    return(1);
  }
}

