/* rng/rng.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_rng.h>

gsl_rng *
gsl_rng_alloc (const gsl_rng_type * T)
{

  gsl_rng *r = (gsl_rng *) malloc (sizeof (gsl_rng));

  if (r == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for rng struct",
                        GSL_ENOMEM, 0);
    };

  r->state = malloc (T->size);

  if (r->state == 0)
    {
      free (r);         /* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for rng state",
                        GSL_ENOMEM, 0);
    };

  r->type = T;

  gsl_rng_set (r, gsl_rng_default_seed);        /* seed the generator */

  return r;
}

int
gsl_rng_memcpy (gsl_rng * dest, const gsl_rng * src)
{
  if (dest->type != src->type)
    {
      GSL_ERROR ("generators must be of the same type", GSL_EINVAL);
    }

  memcpy (dest->state, src->state, src->type->size);

  return GSL_SUCCESS;
}

gsl_rng *
gsl_rng_clone (const gsl_rng * q)
{
  gsl_rng *r = (gsl_rng *) malloc (sizeof (gsl_rng));

  if (r == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for rng struct",
                        GSL_ENOMEM, 0);
    };

  r->state = malloc (q->type->size);

  if (r->state == 0)
    {
      free (r);         /* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for rng state",
                        GSL_ENOMEM, 0);
    };

  r->type = q->type;

  memcpy (r->state, q->state, q->type->size);

  return r;
}

void
gsl_rng_set (const gsl_rng * r, unsigned long int seed)
{
  (r->type->set) (r->state, seed);
}

#ifndef HIDE_INLINE_STATIC
unsigned long int
gsl_rng_get (const gsl_rng * r)
{
  return (r->type->get) (r->state);
}

double
gsl_rng_uniform (const gsl_rng * r)
{
  return (r->type->get_double) (r->state);
}

double
gsl_rng_uniform_pos (const gsl_rng * r)
{
  double x ;
  do
    {
      x = (r->type->get_double) (r->state) ;
    }
  while (x == 0) ;

  return x ;
}

/* Note: to avoid integer overflow in (range+1) we work with scale =
   range/n = (max-min)/n rather than scale=(max-min+1)/n, this reduces
   efficiency slightly but avoids having to check for the out of range
   value.  Note that range is typically O(2^32) so the addition of 1
   is negligible in most usage. */

unsigned long int
gsl_rng_uniform_int (const gsl_rng * r, unsigned long int n)
{
  unsigned long int offset = r->type->min;
  unsigned long int range = r->type->max - offset;
  unsigned long int scale;
  unsigned long int k;

  if (n > range || n == 0)
    {
      GSL_ERROR_VAL ("invalid n, either 0 or exceeds maximum value of generator",
                     GSL_EINVAL, 0) ;
    }

  scale = range / n;

  do
    {
      k = (((r->type->get) (r->state)) - offset) / scale;
    }
  while (k >= n);

  return k;
}
#endif

unsigned long int
gsl_rng_max (const gsl_rng * r)
{
  return r->type->max;
}

unsigned long int
gsl_rng_min (const gsl_rng * r)
{
  return r->type->min;
}

const char *
gsl_rng_name (const gsl_rng * r)
{
  return r->type->name;
}

size_t
gsl_rng_size (const gsl_rng * r)
{
  return r->type->size;
}

void *
gsl_rng_state (const gsl_rng * r)
{
  return r->state;
}

void
gsl_rng_print_state (const gsl_rng * r)
{
  size_t i;
  unsigned char *p = (unsigned char *) (r->state);
  const size_t n = r->type->size;

  for (i = 0; i < n; i++)
    {
      /* FIXME: we're assuming that a char is 8 bits */
      printf ("%.2x", *(p + i));
    }

}

void
gsl_rng_free (gsl_rng * r)
{
  free (r->state);
  free (r);
}


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <mpi.h>
#include <iniparser.h>
#include "prepmt/prepmt_hpulse96.h"
#include "prepmt/prepmt_event.h"
#ifdef PARMT_USE_INTEL
#include <mkl_cblas.h>
#else
#include <cblas.h>
#endif
#include "sacio.h"
#include "cps.h"
#include "cps_mpi.h"
#include "cps_utils.h"
#include "cps_defaults.h"
#include "iscl/array/array.h"
#include "iscl/memory/memory.h"
#include "iscl/os/os.h"
#include "iscl/string/string.h"

#define PROGRAM_NAME "hspec96"

static void grns2sac(const struct hwave_greens_struct grns,
                     struct sacData_struct sac[10]);
struct precompute_struct
{
    char sourceModel[PATH_MAX]; /*!< Source model */
    char crustDir[PATH_MAX];  /*!< Crust1.0 directory */
    char hspecDistanceFile[PATH_MAX];  /*!< Table of distances for hspec */
    char hspecArchiveFile[PATH_MAX];  /*!< Name of archive file with ff green's
                                           functions. */
    double *depths; /*!< Depths (km) at which to compute greens fns */
    double *dists;  /*!< Distances (degrees) at which to compute
                         surface wave greens fns */
    double slat;    /*!< Source latitude (degrees) */
    double slon;    /*!< Source longitude (degrees) */
    double dt;   /*!< Sampling period */
    int npts;    /*!< Number of samples */
    int ndepths; /*!< Number of depths at which to compute greens fns */
    int ndists;  /*!< Number of distances to compute surface waves */
    bool luseSourceMod; /*!< If true then use the local source model */
    bool luseCrust; /*!< If true then use crust1.0 at teleseismic distances */
    bool luseDistanceTable; /*!< If true then use a distance table.
                                 Otherwise linearly interpolate distances */
};
int prepmt_hspec96_readParameters(const char *iniFile,
                                  const char *section,
                                  struct precompute_struct *precompute);
int prepmt_hspec96_initializeArchive(
    const char *archiveName, const char *model,
    const int ndepths, const double *__restrict__ depths,
    const int ngcarcs,  const double *__restrict__ gcarcs);
int prepmt_hspec96_readHspec96Parameters(
    const char *iniFile,
    struct hprep96_parms_struct *hprepParms,
    struct hspec96_parms_struct *hspecParms,
    struct hpulse96_parms_struct *hpulseParms);
static void printUsage(void);
static int parseArguments(int argc, char *argv[], char iniFile[PATH_MAX]);

/*!
 * @brief Makes the regional fullwaveform fundamental fault Green's
 *        functions archive.
 *
 */
int main(int argc, char **argv)
{
    char fname[PATH_MAX], iniFile[PATH_MAX], groupName[512], *modelName;
    struct prepmtEventParms_struct event;
    struct sacData_struct *sac;
    struct precompute_struct precompute;
    struct hwave_greens_struct *grns;
    struct hpulse96_data_struct *zresp;
    struct hprep96_parms_struct hprepParms;
    struct hspec96_parms_struct hspecParms;
    struct hpulse96_parms_struct hpulseParms;
    struct vmodel_struct recmod, vmodel;
    double *depthr, *depths, *r, *tshift, *vred, dt;
    hid_t groupID, h5fl;
    int idep, idist, ierr, k, l, myid, ndeps, ndists, nprocs, npts, nwaves;
    // Here's the idea: I want to input magnitudes with units of N-m so:
    // N-m -> Dyne-dm (1.e+7)
    // CPS internally scales from Dyne-cm to cm (1.e+20)
    // Finally, I want outputs proprotional to m (1.e-2)
    const double xmom = 1.0;     // no confusing `relative' magnitudes
    const double xcps = 1.e-20;  // convert dyne-cm mt to output cm
    const double cm2m = 1.e-2;   // cm to meters
    const double dcm2nm = 1.e+7; // magnitudes intended to be specified in
                                 // Dyne-cm but I work in N-m
    const char *cfaults[10] = {"ZDS\0", "ZSS\0", "ZDD", "ZEX\0",
                               "RDS\0", "RSS\0", "RDD", "REX\0",
                               "TDS\0", "TSS\0"};
    // Given a M0 in Newton-meters get a seismogram in meters
    const double xscal = xmom*xcps*cm2m*dcm2nm;
    const int master = 0;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
    sac = NULL;
    grns = NULL;
    zresp = NULL;
    modelName = NULL;
    r = NULL;
    depthr = NULL;
    depths = NULL;
    vred = NULL;
    tshift = NULL;
    memset(&hprepParms, 0, sizeof(struct hprep96_parms_struct));
    memset(&hspecParms, 0, sizeof(struct hspec96_parms_struct));
    memset(&hpulseParms, 0, sizeof(struct hpulse96_parms_struct));
    memset(&vmodel, 0, sizeof(struct vmodel_struct));
    if (myid == master)
    {
        // Parse the input arguments
        ierr = parseArguments(argc, argv, iniFile);
        if (ierr != 0)
        {
            if (ierr !=-1)
            {
                printf("%s: Failed to parse input commands\n", PROGRAM_NAME);
            }
            goto FINISH_EARLY;
        }
        // Read the CPS variables
        ierr = prepmt_hspec96_readHspec96Parameters(iniFile,
                                                    &hprepParms, &hspecParms,
                                                    &hpulseParms);
        if (ierr != 0)
        {
            printf("Error reading hprep parameters\n");
            MPI_Abort(MPI_COMM_WORLD, 30);
        }
        // Read the additional contrived forward modeling variables
        ierr = prepmt_hspec96_readParameters(iniFile,
                                             "precompute", &precompute);
        if (ierr != 0)
        {
            printf("Error reading precompute parameters\n");
            MPI_Abort(MPI_COMM_WORLD, 30);
        }
        ndists = precompute.ndists;
        ndeps = precompute.ndepths;
        // expand to make a grid
        r = (double *) calloc((size_t) (ndists*ndeps), sizeof(double));
        depths = (double *) calloc((size_t) (ndists*ndeps), sizeof(double));
        for (idep=0; idep<ndeps; idep++)
        {
            for (idist=0; idist<ndists; idist++)
            {
                k = idist*ndeps + idep;
                r[k] = precompute.dists[idist]*111.195;
                depths[k] = precompute.depths[idep];
                printf("Wavform %d has distance %f (deg) and depth %f (km)\n",
                       k+1, r[k]/111.195, depths[k]);
            }
        }
        npts = precompute.npts;
        dt = precompute.dt;
//ndists = 31;
//ndeps = 26;
//npts = 1024; //2048;
//dt = 1.0; //0.5;
        printf("%d %f %s\n", npts, dt, hprepParms.mfile);
        // load the model into memory
        if (precompute.luseSourceMod)
        {
            printf("%s: Loading source model %s...\n",
                   PROGRAM_NAME, precompute.sourceModel);
            strcpy(hprepParms.mfile, precompute.sourceModel);
            ierr = cps_getmod(hprepParms.mfile, &vmodel);
            if (ierr != 0)
            {
                printf("Failed to load source model\n");
                goto FINISH_EARLY;
            }
        }
        else if (precompute.luseCrust)
        {
            ierr = prepmt_event_initializeFromIniFile(iniFile, &event);
            if (ierr != 0)
            {
                printf("%s: Error getting event info\n", PROGRAM_NAME);
                goto FINISH_EARLY;
            }
            printf("%s: Loading CPS model (lat,lon)=(%f,%f)\n", PROGRAM_NAME,
                   event.latitude, event.longitude);
            ierr = cps_crust1_getCrust1ForHerrmann(precompute.crustDir, false,
                                              event.latitude, event.longitude,
                                              &event.latitude, &event.longitude,
                                              1, &vmodel, &recmod);
            cps_utils_freeVmodelStruct(&recmod);
        }
        else
        {
            printf("%s: Model is not set\n", PROGRAM_NAME);
            ierr = 1;
            goto FINISH_EARLY;
        }
        // Initialize the archive - remove NULL terminator
        modelName = string_strip(NULL, vmodel.title);
        printf("%s: Initializing archive file %s with model %s\n",
               PROGRAM_NAME, precompute.hspecArchiveFile, modelName);
        ierr = prepmt_hspec96_initializeArchive(precompute.hspecArchiveFile,
                                                modelName,
                                                ndeps, depths,
                                                ndists, precompute.dists);
        if (ierr != 0)
        {
            printf("%s: Failed to initialize archive\n", PROGRAM_NAME);
            goto FINISH_EARLY;
        }
        free(precompute.depths);
        free(precompute.dists);
    }
FINISH_EARLY:;
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Bcast(&ierr, 1, MPI_INTEGER, master, MPI_COMM_WORLD);
    if (ierr != 0){goto FINISH;}
    cps_broadcast_vmodelStruct(MPI_COMM_WORLD, master, &vmodel);
    cps_broadcast_hprep96ParmsStruct(MPI_COMM_WORLD, master, &hprepParms);
    cps_broadcast_hspec96ParmsStruct(MPI_COMM_WORLD, master, &hspecParms);
    cps_broadcast_hpulse96ParmsStruct(MPI_COMM_WORLD, master, &hpulseParms);
    MPI_Bcast(&ndists, 1, MPI_INTEGER, master, MPI_COMM_WORLD);
    MPI_Bcast(&ndeps,  1, MPI_INTEGER, master, MPI_COMM_WORLD);
    MPI_Bcast(&npts, 1, MPI_INTEGER, master, MPI_COMM_WORLD);
    MPI_Bcast(&dt, 1, MPI_DOUBLE, master, MPI_COMM_WORLD);
    // initialize receiver information
    nwaves = ndists*ndeps;
    depthr = (double *) calloc((size_t) nwaves, sizeof(double));
    if (myid != master)
    {
        depths = (double *) calloc((size_t) nwaves, sizeof(double));
        r = (double *) calloc((size_t) nwaves, sizeof(double));
    }
    tshift = (double *) calloc((size_t) nwaves, sizeof(double));
    vred = (double *) calloc((size_t) nwaves, sizeof(double));
    MPI_Bcast(depths, nwaves, MPI_DOUBLE, master, MPI_COMM_WORLD);
    MPI_Bcast(r, nwaves, MPI_DOUBLE, master, MPI_COMM_WORLD);
    // set the receiver distances
//r[0] = 55.597; //111.195;
//r[1] = 111.195;
//r[2] = 166.792;
//depths[0] = 7.0;
//for (idep=0; idep<ndeps; idep++)
//{
//    for (idist=0;  idist<ndists; idist++)
//    {
//        k = idist*ndeps + idep;
//        r[k] = 55.597*(double) (idist + 1);
//        depths[k] = (double) idep;
//    }
//}
/*
printf("premature end\n");
MPI_Finalize();
return 0;
*/
    // call hspec
    zresp = hspec96_mpi_interface(MPI_COMM_WORLD, nwaves, npts, dt,
                                  r, tshift, vred, depthr, depths,
                                  hprepParms, hspecParms,
                                  vmodel, &ierr);
    MPI_Barrier(MPI_COMM_WORLD);
    if (ierr != 0)
    {
        printf("Error calling hspec\n");
        goto FINISH;
    }
    // convolve STF and convert back to time domain
    grns = hpulse96_mpi_interface(MPI_COMM_WORLD, nwaves, hpulseParms,
                                  zresp, &ierr);
    if (ierr != 0)
    {
        printf("Error computing greens fns\n");
        goto FINISH;
    }
    // dump the results
    if (myid == master)
    {
        printf("Writing results...\n");
        h5fl = H5Fopen(precompute.hspecArchiveFile, H5F_ACC_RDWR, H5P_DEFAULT);
        sac = (struct sacData_struct *)
              calloc(10, sizeof(struct sacData_struct));
        k = 0;
        //for (idist=0; idist<ndists; idist++)
        for (idep=0; idep<ndeps; idep++)
        {
            memset(fname, 0, PATH_MAX*sizeof(char));
            sprintf(fname, "dump/depth_%d", idep);
            os_makedirs(fname);

            memset(groupName, 0, 512*sizeof(char));
            sprintf(groupName, "/%s/Depth_%d", modelName, idep);
            groupID = H5Gopen(h5fl, groupName, H5P_DEFAULT);
            //for (idep=0; idep<ndeps; idep++)
            for (idist=0; idist<ndists; idist++)
            {
                k = idist*ndeps + idep;
                grns2sac(grns[k], sac);
                memset(fname, 0, PATH_MAX*sizeof(char));
                sprintf(fname, "dump/depth_%d/%04d01ZDD.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[0]);
                sprintf(fname, "dump/depth_%d/%04d02RDD.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[1]);
                sprintf(fname, "dump/depth_%d/%04d03ZDS.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[2]);
                sprintf(fname, "dump/depth_%d/%04d04RDS.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[3]);
                sprintf(fname, "dump/depth_%d/%04d05TDS.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[4]);
                sprintf(fname, "dump/depth_%d/%04d06ZSS.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[5]);
                sprintf(fname, "dump/depth_%d/%04d07RSS.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[6]);
                sprintf(fname, "dump/depth_%d/%04d08TSS.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[7]);
                sprintf(fname, "dump/depth_%d/%04d09ZEX.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[8]);
                sprintf(fname, "dump/depth_%d/%04d10REX.SAC", idep, idist+1);
                sacio_writeTimeSeriesFile(fname, sac[9]);
                // Dump to the H5 archive and release memory
                for (l=0; l<10; l++)
                {
                    // Rescale to unit magnitude
                    cblas_dscal(sac[l].npts, xscal, sac[l].data, 1);
                    sprintf(fname, "Greens_%s_%d", cfaults[l], idist);
                    sacioh5_writeTimeSeries2(fname, groupID, sac[l]);
                    sacio_free(&sac[l]);
                }
//break;
            }
            H5Gclose(groupID);
//break;
        }
        if (sac != NULL){free(sac);}
        H5Fclose(h5fl);
        printf("%s: Finishing program\n", PROGRAM_NAME);
    }
    MPI_Barrier(MPI_COMM_WORLD);
FINISH:;
    if (grns != NULL)
    {
        for (k=0; k<nwaves; k++)
        {
            cps_utils_freeHwaveGreensStruct(&grns[k]);
        }
        free(grns);
        grns = NULL;
    }
    if (zresp != NULL)
    {
        for (k=0; k<nwaves; k++)
        {
            cps_utils_freeHpulse96DataStruct(&zresp[k]);
        }
        free(zresp);
        zresp = NULL;
    }
    // release memory
    memory_free8c(&modelName);
    free(depthr);
    free(depths);
    free(r);
    free(tshift);
    free(vred);
    cps_utils_freeHpulse96ParmsStruct(&hpulseParms);
    cps_utils_freeVmodelStruct(&vmodel);
    MPI_Finalize();
    return 0;
}
//============================================================================//
int prepmt_hspec96_initializeArchive(
    const char *archiveName, const char *model,
    const int ndepths, const double *__restrict__ depths,
    const int ngcarcs,  const double *__restrict__ gcarcs)
{
    char modelName[512], depthName[512]; //, varName[512];
    hid_t h5fl, dataSet, dataSpace, depthGroup, modelGroup;
    hsize_t dims[1];
    int idep;
    //------------------------------------------------------------------------//
    h5fl = H5Fcreate(archiveName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    memset(modelName, 0, 512*sizeof(char));
    sprintf(modelName, "/%s", model);
    printf("%s: Initializing model: %s\n", __func__, modelName);
    modelGroup = H5Gcreate2(h5fl, modelName, H5P_DEFAULT,
                            H5P_DEFAULT, H5P_DEFAULT);
    // write the depths and distances
    dims[0] = (hsize_t) ndepths;
    dataSpace = H5Screate_simple(1, dims, NULL);
    dataSet = H5Dcreate2(modelGroup, "Depths\0", H5T_NATIVE_DOUBLE,
                         dataSpace,
                         H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    H5Dwrite(dataSet, H5T_NATIVE_DOUBLE, H5S_ALL,
             H5S_ALL, H5P_DEFAULT, depths);
    H5Dclose(dataSet);
    H5Sclose(dataSpace);
    // Write the distances
    dims[0] = (hsize_t) ngcarcs;
    dataSpace = H5Screate_simple(1, dims, NULL);
    dataSet = H5Dcreate2(modelGroup, "Distances\0", H5T_NATIVE_DOUBLE,
                         dataSpace,
                         H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    H5Dwrite(dataSet, H5T_NATIVE_DOUBLE, H5S_ALL,
             H5S_ALL, H5P_DEFAULT, gcarcs);
    H5Dclose(dataSet);
    H5Sclose(dataSpace);
    // Create the depths groups
    for (idep=0; idep<ndepths; idep++)
    {
        memset(depthName, 0, 512*sizeof(char));
        sprintf(depthName, "%s/Depth_%d", modelName, idep);
        depthGroup = H5Gcreate2(h5fl, depthName, H5P_DEFAULT,
                                H5P_DEFAULT, H5P_DEFAULT);
        H5Gclose(depthGroup);
    }
    H5Gclose(modelGroup);
    H5Fclose(h5fl);
    return 0;
}
//============================================================================//
int prepmt_hspec96_readParameters(const char *iniFile,
                                  const char *section,
                                  struct precompute_struct *precompute)
{
    const char *fcnm = "prepmt_hspec96_readParameters\0";
    FILE *dfl;
    const char *s;
    char *dirName, cline[256], vname[128];
    double depth0, depth1, dist0, dist1;
    int i, ierr;
    dictionary *ini;
    memset(precompute, 0, sizeof(struct precompute_struct));
    if (!os_path_isfile(iniFile))
    {
        printf("%s: ini file: %s does not exist\n", fcnm, iniFile);
        return -1;
    }
    ini = iniparser_load(iniFile);
    if (ini == NULL)
    {
        printf("%s: Cannot parse ini file\n", fcnm);
        return -1;
    }
    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:ndepths", section);
    precompute->ndepths = iniparser_getint(ini, vname, 1);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:depthMin", section);
    depth0 = iniparser_getdouble(ini, vname, 1.0);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:depthMax", section);
    depth1 = iniparser_getdouble(ini, vname, 1.0);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:ndists", section);
    precompute->ndists = iniparser_getint(ini, vname, 1);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:minDistance", section);
    dist0 = iniparser_getdouble(ini, vname, 1.0);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:maxDistance", section);
    dist1 = iniparser_getdouble(ini, vname, 1.0);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:dtHspec", section);
    precompute->dt = iniparser_getdouble(ini, vname, 1.0);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:nptsHspec", section);
    precompute->npts = iniparser_getint(ini, vname, 1024);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:luseCrust1", section);
    precompute->luseCrust = iniparser_getboolean(ini, vname, 0);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:luseSourceModel", section);
    precompute->luseSourceMod = iniparser_getboolean(ini, vname, 1);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:sourceModel", section);
    s = iniparser_getstring(ini, vname, NULL);
    if (s != NULL)
    {
        strcpy(precompute->sourceModel, s);
        if (!os_path_isfile(precompute->sourceModel) &&
            precompute->luseSourceMod)
        {
            printf("%s: Source model doesn't %s exist\n", fcnm, s);
            precompute->luseSourceMod = false;
        }
    }
    else
    {
        precompute->luseSourceMod = false;
    }

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:hspecArchiveFile", section);
    s = iniparser_getstring(ini, vname, "hspecFFgreens.h5");
    dirName = os_dirname(s, &ierr);
    if (!os_path_isdir(dirName))
    {
        ierr = os_makedirs(dirName);
        if (ierr != 0)
        {
            printf("%s: Failed to make output directory: %s\n", fcnm, dirName);
            return -1;
        }
    }
    memory_free8c(&dirName);
    strcpy(precompute->hspecArchiveFile, s);

    //memset(vname, 0, 128*sizeof(char));
    //sprintf(vname, "%s:modelIdentifier", section);
    //s = iniparser_getstring(ini, vname, "UNKNOWN");
    //strcpy(precompute->modelIdentifer, s);

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:crustDir", section);
    s = iniparser_getstring(ini, vname, CPS_DEFAULT_CRUST1_DIRECTORY);
    if (s != NULL)
    {
        strcpy(precompute->crustDir, s);
        if (!os_path_isdir(precompute->crustDir) && precompute->luseCrust)
        {
            printf("%s: crust1.0 directory %s doesn't exist\n", fcnm, s);
            precompute->luseCrust = false;
        }
    }
    else
    {
        precompute->luseCrust = false;
    }

    memset(vname, 0, 128*sizeof(char));
    sprintf(vname, "%s:luseDistanceTable", section);
    precompute->luseDistanceTable = iniparser_getboolean(ini, vname, false);
    if (precompute->luseDistanceTable)
    {
        memset(vname, 0, 128*sizeof(char));
        sprintf(vname, "%s:hspecDistanceFile", section);
        s = iniparser_getstring(ini, vname, NULL);
        if (!os_path_isfile(s))
        {
            printf("%s: files %s doesn't exist\n", fcnm, s);
            return -1;
        }
        strcpy(precompute->hspecDistanceFile, s);
        dfl = fopen(s, "r");
        memset(cline, 0, 256*sizeof(char));
        fgets(cline, 256, dfl);
        memset(cline, 0, 256*sizeof(char));
        fgets(cline, 256, dfl);
        precompute->ndists = atoi(cline);
        if (precompute->ndists < 1)
        {
            printf("%s: Invalid number of distances\n", fcnm);
            return -1;
        }
        precompute->dists
            = (double *) calloc((size_t) precompute->ndists, sizeof(double));
        for (i=0; i<precompute->ndists; i++)
        {
            memset(cline, 0, 256*sizeof(char));
            if (fgets(cline, 256, dfl) == NULL)
            {
                printf("%s: premature end of distance file\n", fcnm);
                return -1;
            }
            precompute->dists[i] = atof(cline);
        }
        fclose(dfl);
    }
    else
    {
        precompute->dists
            = (double *) calloc((size_t) precompute->ndists, sizeof(double));
        ierr = array_linspace64f_work(dist0, dist1,
                                      precompute->ndists, precompute->dists);
    }
    // depths
    precompute->depths
        = (double *) calloc((size_t) precompute->ndepths, sizeof(double));
    ierr = array_linspace64f_work(depth0, depth1,
                                  precompute->ndepths, precompute->depths);
    iniparser_freedict(ini);
    return ierr;
}
//============================================================================//
int prepmt_hspec96_readHspec96Parameters(
    const char *iniFile,
    struct hprep96_parms_struct *hprepParms,
    struct hspec96_parms_struct *hspecParms,
    struct hpulse96_parms_struct *hpulseParms)
{
    const char *fcnm = "prepmt_hspec96_readHspec96Parameters\0";
    int ierr;
    cps_setHprep96Defaults(hprepParms);
    cps_setHspec96Defaults(hspecParms);
    ierr = prepmt_hpulse96_readHpulse96Parameters(iniFile,
                                                  "hpulse96\0", hpulseParms);
    if (ierr != 0)
    {
        printf("%s: Error reading hpulse parameters\n", fcnm);
    }
    return ierr;
}
//============================================================================//
/*!
 * @brief Parses the command line arguments for the ini file.
 *
 * @param[in] argc      Number of arguments input to the program.
 * @param[in] argv      Command line arguments.
 *
 * @param[out] iniFile  If the result is 0 then this is the ini file.
 *
 * @result 0 indicates success. \n
 *        -1 indicates the user inquired about the usage. \n
 *        -2 indicates the command line argument is invalid.
 *
 * @author Ben Baker, ISTI
 *
 */
static int parseArguments(int argc, char *argv[], char iniFile[PATH_MAX])
{
    bool linFile;
    linFile = false;
    memset(iniFile, 0, PATH_MAX*sizeof(char));
    while (true)
    {
        static struct option longOptions[] =
        {
            {"help", no_argument, 0, '?'},
            {"help", no_argument, 0, 'h'},
            {"ini_file", required_argument, 0, 'i'},
            {"section", required_argument, 0, 's'},
            {0, 0, 0, 0}
        };
        int c, optionIndex;
        c = getopt_long(argc, argv, "?hi:",
                        longOptions, &optionIndex);
        if (c ==-1){break;}
        if (c == 'i')
        {
            strcpy(iniFile, (const char *) optarg);
            linFile = true;
        }
        else if (c == 'h' || c == '?')
        {
            printUsage();
            return -2;
        }
        else
        {
            printf("%s: Unknown options: %s\n",
                   PROGRAM_NAME, argv[optionIndex]);
        }
    }
    if (!linFile)
    {
        printf("%s: Error must specify ini file\n\n", PROGRAM_NAME);
        printUsage();
        return -1;
    }
    else
    {
        if (!os_path_isfile(iniFile))
        {
            printf("%s: Error - ini file: %s does not exist\n",
                   PROGRAM_NAME, iniFile);
            return EXIT_FAILURE;
        }
    }
    return 0;
}
//============================================================================//
static void printUsage(void)
{
    printf("Usage:\n   %s -i iniFile\n", PROGRAM_NAME);
    printf("or\n");
    printf("    mpirun -np nProcessors %s -i iniFile\n", PROGRAM_NAME);
    printf("Required arguments:\n");
    printf("    -i ini_file specifies the initialization file\n");
    printf("Optional arguments:\n");
    printf("    -h displays this message\n");
    printf("    -np Number of computer processors to use\n");
    return;
}
//============================================================================//
static void grns2sac(const struct hwave_greens_struct grns,
                     struct sacData_struct sac[10])
{
    const char *kcmpnm[10] = {"ZDD\0", "RDD\0", "ZDS\0", "RDS\0", "TDS\0",
                              "ZSS\0", "RSS\0", "TSS\0", "ZEX\0", "REX\0"};
    double cmpinc, cmpaz;
    int i, k;
    bool lsh;
    for (k=0; k<10; k++)
    {
        memset(&sac[k], 0, sizeof(struct sacData_struct));
        sacio_setDefaultHeader(&sac[k].header);
        lsh = false;
        sac[k].header.npts = grns.npts;
        //sac[k].header.b = grns.b;
        sac[k].npts = grns.npts;
        sac[k].header.b = grns.t0;
        sac[k].header.e = grns.t0 + (double) (grns.npts - 1)*grns.dt;
        sac[k].header.delta = grns.dt;
        sac[k].header.dist = grns.dist;
        sac[k].header.evdp = grns.evdep;
        sac[k].header.stel = grns.stelel;
        sac[k].header.gcarc = grns.dist/111.195;
        sac[k].header.o =-grns.t0;
        strcpy(sac[k].header.kevnm, "SYNTHETIC");
        strcpy(sac[k].header.ko, "O\0");
        sac[k].header.a = grns.timep;
        strcpy(sac[k].header.ka, "P\0");
        sac[k].header.az = 0.0;
        sac[k].header.baz = 180.0;
        sac[k].header.lcalda = 0;
        sac[k].header.norid = 0;
        sac[k].header.nevid = 0;
        sac[k].header.nzyear = 1970;
        sac[k].header.nzjday = 1;
        sac[k].header.nzhour = 0;
        sac[k].header.nzmin = 0;
        sac[k].header.nzsec = 0;
        sac[k].header.nzmsec = 0;
        sac[k].header.lhaveHeader = true;
        strcpy(sac[k].header.kcmpnm, kcmpnm[k]);
        sac[k].data = (double *) calloc((size_t) grns.npts, sizeof(double));
        cmpaz = 0.0;
        cmpinc = 0.0;
        if (k == 0)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.zdd[i];}
        }
        else if (k == 1)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.rdd[i];}
            cmpinc = 90.0;
        }
        else if (k == 2)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.zds[i];}
        }
        else if (k == 3)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.rds[i];}
            cmpinc = 90.0;
        }
        else if (k == 4)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.tds[i];}
            lsh = true;
            cmpaz = 90.0;
            cmpinc = 90.0;
        }
        else if (k == 5)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.zss[i];}
        }
        else if (k == 6)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.rss[i];}
            cmpinc = 90.0;
        }
        else if (k == 7)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.tss[i];}
            lsh = true;
            cmpaz = 90.0;
            cmpinc = 90.0;
        }
        else if (k == 8)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.zex[i];}
        }
        else if (k == 9)
        {
            for (i=0; i<grns.npts; i++){sac[k].data[i] = grns.rex[i];}
            cmpinc = 90.0;
        }
        if (lsh)
        {
            sac[k].header.t1 = grns.timesh;
            strcpy(sac[k].header.kt0, "SH\0");
        }
        else
        {
            sac[k].header.t1 = grns.timesv;
            strcpy(sac[k].header.kt0, "SV\0");
        }
        sac[k].header.cmpaz = cmpaz;
        sac[k].header.cmpinc = cmpinc;
    }
    return;
}


#if !defined(PETIGABL_H)
#define PETIGABL_H

#include <petsc.h>
#include <petscblaslapack.h>
#if defined(PETSC_BLASLAPACK_UNDERSCORE)
   #define sgetri_ sgetri_
   #define dgetri_ dgetri_
   #define qgetri_ qgetri_
   #define cgetri_ cgetri_
   #define zgetri_ zgetri_
#elif defined(PETSC_BLASLAPACK_CAPS)
   #define sgetri_ SGETRI
   #define dgetri_ DGETRI
   #define qgetri_ QGETRI
   #define cgetri_ CGETRI
   #define zgetri_ ZGETRI
#else /* (PETSC_BLASLAPACK_C) */
   #define sgetri_ sgetri
   #define dgetri_ dgetri
   #define qgetri_ qgetri
   #define cgetri_ cgetri
   #define zgetri_ zgetri
#endif
#if !defined(PETSC_USE_COMPLEX)
  #if defined(PETSC_USE_REAL_SINGLE)
    #define LAPACKgetri_ sgetri_
  #elif defined(PETSC_USE_REAL_DOUBLE)
    #define LAPACKgetri_ dgetri_
  #else /* (PETSC_USE_REAL_QUAD) */
    #define LAPACKgetri_ qgetri_
  #endif
#else
  #if defined(PETSC_USE_REAL_SINGLE)
    #define LAPACKgetri_ cgetri_
  #elif defined(PETSC_USE_REAL_DOUBLE)
    #define LAPACKgetri_ zgetri_
  #else /* (PETSC_USE_REAL_QUAD) */
    #define LAPACKgetri_ wgetri_
  #endif
#endif
EXTERN_C_BEGIN
extern void LAPACKgetri_(PetscBLASInt*,PetscScalar*,PetscBLASInt*,
                         PetscBLASInt*,PetscScalar*,PetscBLASInt*,
                         PetscBLASInt*);
EXTERN_C_END

#if PETSC_VERSION_LE(3,3,0)
#undef PetscBLASIntCast
#undef  __FUNCT__
#define __FUNCT__ "PetscBLASIntCast"
PETSC_STATIC_INLINE PetscErrorCode PetscBLASIntCast(PetscInt a,PetscBLASInt *b)
{
  PetscFunctionBegin;
#if defined(PETSC_USE_64BIT_INDICES) && !defined(PETSC_HAVE_64BIT_BLAS_INDICES)
  if ((a) > PETSC_BLAS_INT_MAX) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Array too long for BLAS/LAPACK");
#endif
  *b =  (PetscBLASInt)(a);
  PetscFunctionReturn(0);
}
#endif

#endif/*PETIGABL_H*/


//  This random generator is a C++ wrapper for the GNU Scientific Library
//  Copyright (C) 2001 Torbjorn Vik

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifndef __histogram_h
#define __histogram_h

#include <gsl/gsl_histogram.h>
#include <stdexcept>
#include <exception>

namespace gsl{
#ifndef __HP_aCC
using std::string;
using std::runtime_error;
#endif

//! Encapsulates the histogram object of gsl. Only uniformly spaced bins yet.
class histogram
{
public:
	histogram(int nBins, double xmin, double xmax)
	{
		h=gsl_histogram_calloc(nBins);
		if (!h)
		{
			throw runtime_error("Couldn't allocate memory for histogram");
		}
		gsl_histogram_set_ranges_uniform(h, xmin, xmax);
	}
	~histogram(){gsl_histogram_free(h);}

//@{ Updating and Accessing Methods
	int increment(double x){return gsl_histogram_increment(h, x);}
	int accumulate(double x, double weight){return gsl_histogram_accumulate(h, x, weight);}
	double get(int i) const {return gsl_histogram_get(h, i);}
	double& operator[](const uint & i)
	{
		const uint n = h->n;

		if (i >= n)
		{
			throw runtime_error("index lies outside valid range of 0 .. n - 1");
//			GSL_ERROR_VAL ("index lies outside valid range of 0 .. n - 1", GSL_EDOM, 0);
		}

		return h->bin[i];
	}
	const double& operator[](const uint & i) const //{return (*this)[i];/*gsl_histogram_get(h, i);*/}
	{
		const uint n = h->n;

		if (i >= n)
		{
			throw runtime_error("index lies outside valid range of 0 .. n - 1");
//			GSL_ERROR_VAL ("index lies outside valid range of 0 .. n - 1", GSL_EDOM, 0);
		}

		return h->bin[i];
	}

	void get_range(int i, double& xmin, double& xmax) const {gsl_histogram_get_range(h, i, &xmin, &xmax);}
//@}

//@{These functions return the maximum upper and minimum lower range limits
// and the number of bins of the histogram h. They provide a way of determining these values without
//    accessing the gsl_histogram struct directly.
	double max() const {return gsl_histogram_max(h);}
	double min() const {return gsl_histogram_min(h);}
	int bins()const {return gsl_histogram_bins(h);}
	int size()const {return gsl_histogram_bins(h);}

//@}

//@{ Histogram statistics
	double mean()const {return gsl_histogram_mean(h);} // not in gsl library ?
	double max_val() const {return gsl_histogram_max_val(h);}
	int max_bin() const {return gsl_histogram_max_bin(h);}
	double min_val() const {return gsl_histogram_min_val(h);}
	int min_bin() const {return gsl_histogram_min_bin(h);}
	double sum() const {return gsl_histogram_sum(h);}
//@}

//@{ Accessor for gsl compatibility
	gsl_histogram*       gslobj()       { return h;}
	const gsl_histogram* gslobj() const { return h;}
//@}

protected:
	gsl_histogram * h;
};
}

#endif // __histogram_h


// A cascade of IntegralHOGDetectors.
//
// Copyright 2012 Mark Desnoyer (mdesnoyer@gmail.com)

#ifndef __HOG_DETECTOR_INTEGRAL_HOG_CASCADE_TRAINER_H__
#define __HOG_DETECTOR_INTEGRAL_HOG_CASCADE_TRAINER_H__

#include <ros/ros.h>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <functional>
#include <vector>
#include <string>
#include <queue>
#include <limits>
#include <cmath>
#include <opencv2/core/core.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <gsl/gsl_multimin.h>
#include "hog_detector/integral_hog_cascade.h"
#include "hog_detector/integral_hog_detector.h"
#include "hog_detector/interpolation.h"

namespace hog_detector {

// Class to calculate the various time components of the cascade
class IntegralCascadeTimeCalculator {
public:
  // Creates the calculator based on text files with timing data (in seconds)
  //
  // integralHistTime - single line with the time
  // fillBlockCacheTime - lines of <blockW>,<blockH>,<time>
  // svmEvalTime - lines of <descriptorSize>,<subWinW>,<subWinH>,<time/window>
  // trueHogTime - lines of <nWindows>,<time>
  IntegralCascadeTimeCalculator(const std::string& integralHistTime,
                                const std::string& fillBlockCacheTime,
                                const std::string& svmEvalTime,
                                const std::string& trueHogTime);

  // Time to compute the integral histogram
  double GetIntegralHistTime() const { return integralHistTime_; }

  // Time to fill the block cache for a given block size
  double GetFillCacheTime(const cv::Size& blockSize) const;

  // Time to do the svm evaluation of a single window
  double GetSVMEvalTime(int descriptorSize) const;

  // Time to compute the full HOG detector on N windows
  double GetTrueHogTime(int nWindows) const;

  int GetMaxWindows() const { return maxWindows_; }

  double GetMaxHogTime() const { return GetTrueHogTime(GetMaxWindows()); }

private:
  double integralHistTime_;

  boost::unordered_map<cv::Size, double> cacheFillTime_;

  boost::scoped_ptr<Interpolator> svmTime_;
  boost::scoped_ptr<Interpolator> trueHogTime_;

  int maxWindows_;
};

class IntegralHogCascadeTrainer {
public:
  // Constructor
  //
  // timeCalculator - takes ownership of it
  IntegralHogCascadeTrainer(IntegralCascadeTimeCalculator* timeCalculator,
                            const cv::Size& winSize);

  // Add a candidate IntegralHogDetector.
  void AddCandidateDetector(IntegralHogDetector::Ptr detector) {
    if (detector->winSize() == winSize_) {
      detectorIdx_[detector.get()] = detectors_.size();
      detectors_.push_back(detector);
    } else {
      ROS_WARN_STREAM("The detector has the wrong window size: ("
                      << winSize_.width << ","
                      << winSize_.height  << ") vs. ("
                      << detector->winSize().width << ","
                      << detector->winSize().height << ")");
    }
  }

  // Trains the cascade detector
  //
  // Inputs:
  // filenames - Filenames of images to train with. Uses the center
  //             winSize window in the image.
  // labels - Labels for all the images. 1 is positive, -1 is negative
  // budget - Processing budget in seconds (average per/frame)
  // missCost - The cost of a miss
  // falsePosCost - The cost of a false positive
  // fracNegSampled - fraction of the number of negative examples we're
  //                  actually training with. So the number of negative
  //                  samples seen by this class is trueNegs/fracNegSampled
  void Train(const std::vector<std::string>& filenames,
             const std::vector<float>& labels,
             float budget,
             float missCost,
             float falsePosCost);
  // Train using the supermodular-submodular approach
  void TrainSupSub(const std::vector<std::string>& filenames,
                   const std::vector<float>& label,
                   float missCost,
                   float falsePosCost,
                   float timeCostPerError, // time is per seconds
                   float fracNegSampled);

  // Training can also be done by adding one image at a time and then
  // calling TrainWithLoadedData
  void AddImageForTraining(const cv::Mat& image,
                           const std::vector<cv::Rect>& rois,
                           float label);
  void TrainWithLoadedData(float budget,
                           float missCost,
                           float falsePosCost);
  void TrainSupSubWithLoadedData(float missCost,
                                 float falsePosCost,
                                 float timeCostPerError,
                                 float fracNegSampled);

  const IntegralHogCascade* cascade() const { return cascade_.get(); }

private:
  // Structure to hold the score and selected threshold for a given detector
  struct DetectorStats {
    DetectorStats(float _score, float _thresh, unsigned int _detectorIdx)
      : score(_score), thresh(_thresh), detectorIdx(_detectorIdx) {}

    // Sort primarily based on the score
    bool operator<(const DetectorStats& other) const {
      if (fequal(score, other.score)) {
        if (detectorIdx == other.detectorIdx) {
          return thresh < other.thresh;
        }
        return detectorIdx < other.detectorIdx;
      }
      return score < other.score;
    }
    bool operator==(const DetectorStats& other) const {
      return detectorIdx == other.detectorIdx && fequal(score, other.score)
        && fequal(thresh, other.thresh);
    }
    bool operator>(const DetectorStats& other) const {return other < *this;}

    bool fequal(float a, float b,
                float ep=std::numeric_limits<float>::epsilon()*100) const {
      return std::fabs(a-b) < ep;
    }

    float score;
    float thresh;
    unsigned int detectorIdx;
  };

  // Number of images being trained on
  int nImages_;

  cv::Size winSize_;

  boost::scoped_ptr<IntegralCascadeTimeCalculator> timeCalculator_;

  // All the possible detectors
  std::vector<IntegralHogDetector::Ptr> detectors_;

  // Scores of the training images for all the detectors. Each row is
  // a different detector and each column is a different training
  // image.
  cv::Mat_<float> scores_;

  // Labels for all the loaded images
  cv::Mat_<float> labels_;

  // Inverse index from the detector to its index in scores_ and labels_
  boost::unordered_map<IntegralHogDetector*, int> detectorIdx_;

  // The trained cascade
  boost::scoped_ptr<IntegralHogCascade> cascade_;

  // The true sum of the number of images given that the negatives are
  // sampled more sparsely.
  double trueImageSum_;

  void LoadImagesForTraining(const std::vector<std::string>& filenames,
                             const std::vector<float>& labels);

  // Trains a cascade using a greedy metric.
  //
  // Inputs:
  // budget - Time budget in seconds,
  // mistCost - Cost of a miss
  // falsePosCost - Cost of a false positive
  // metric - The metric to use
  //
  // Outputs:
  // cascade - The trained cascade
  // returns - Total cost of the cascade
  template <typename T>
  float TrainUsingGreedyMetric(float budget, float missCost,
                               float falsePosCost,
                               T metric,
                               IntegralHogCascade* cascade);

  // Two different metrics for selecting the next best detector.
  //
  // Inputs:
  // missCost - Cost of a miss
  // falsePosCost - Cost of a false positive
  // inputWindows - - Boolean matrix identifying which training windows are
  //                still in play. Will be updated
  // allocatedTime - The time already allocated. Will be updated after this
  //                 selection
  // cacheSizesUsed - Set of cache sizes used. Will be updated after this
  //                  selection.
  //
  // Returns:
  // pair of threshold and index of the selected detector
  std::pair<float, int> SelectBestDetectorIgnoringTime(
    float missCost,
    float falsePosCost,
    cv::Mat& inputWindows,
    float& allocatedTime,
    boost::unordered_set<cv::Size>& cacheSizesUsed);

  std::pair<float, int> SelectBestDetectorPerTime(
    float missCost,
    float falsePosCost,
    cv::Mat& inputWindows,
    float& allocatedTime,
    boost::unordered_set<cv::Size>& cacheSizesUsed);

  // Updates all the tracking data for when a detector is chosen
  void ChooseDetector(int detectorI, float thresh,
                      cv::Mat& inputWindows,
                      float& allocatedTime,
                      boost::unordered_set<cv::Size>& cacheSizesUsed);

  // Selects the best detector that is under budget
  void SelectBestDetectorUnderBudget(
    float budget, float missCost,
    float falsePosCost,
    const cv::Mat& inputWindows,
    float allocatedTime,
    const boost::unordered_set<cv::Size>& cacheSizesUsed,
    float* thresh,
    int* chosenDetector,
    float* cost);

  // Selects the best detector according to an approximate submodular measure that is tight at cascade.
  //
  // Inputs:
  //   missCost - Cost of a miss
  //   falsePosCost - Cost of a false positive
  //   timeCostPerError - Conversion factor for 1s of processing time compared
  //                    to an error.
  //   cascade - The best cascade so far. The approximation is tight at
  //               this point.
  //   winPastCascade - Boolean vector specifying the windows that pass the cascade
  //
  // Outputs:
  //  thresh - Threshold of the detector to choose
  //  chosenDetector - Index of the chosen detector
  //  returns - The delta cost for the added detector
  typedef std::priority_queue<DetectorStats, std::vector<DetectorStats>,
                              std::greater<DetectorStats> > DetectorQueue;
  float SelectBestDetectorApprox(
    float missCost,
    float falsePosCost,
    float timeCostPerError,
    float fracNegSampled,
    const IntegralHogCascade& cascade,
    const cv::Mat& winPastApproxCascade,
    DetectorQueue* detectorQueue,
    float* thresh,
    int* chosenDetector);

  // Compute the bayes risk for a given threshold on a set of images
  // whose scores are curScores.
  double ComputeBayesRisk(const cv::Mat& inputWindows,
                          const cv::Mat_<float>& curScores,
                          float thresh,
                          double missCost,
                          double falsePosCost);

  double ComputeProcessingTimeForDetector(
    float fracInputWindows,
    int detectorI,
    const boost::unordered_set<cv::Size>& cacheSizesUsed);

  // For the SupSub proceedure find the cascade that minimizes the
  // approximate cost. The cost is approximate because the D_a and C_M
  // terms are approximated by an upper bound.
  //
  // Inputs:
  // missCost - Cost of a miss
  // falsePosCost - Cost of a false positive, already adjusted with
  //                fracNegSampled
  // timeCostPerError - Conversion factor for 1s of processing time compared
  //                    to an error.
  // bestCascade - The best cascade so far. The approximation is tight at
  //               this point.
  // fracNegSampled - fraction of the number of negative examples we're
  //                  actually training with. So the number of negative
  //                  samples seen by this class is trueNegs/fracNegSampled
  //
  // Outputs:
  // bestCascade - An update
  // Returns - The true cost of the best cascade (not the approximate cost)
  float MinimizeApproxCost(float missCost, float falsePosCost,
                           float timeCostPerError,
                           float fracNegSampled,
                           boost::scoped_ptr<IntegralHogCascade>* ioCascade);

  // Computes the total cost of the cascade
  double ComputeTotalCost(float missCost,
                          float falsePosCost,
                          float timeCostPerError,
                          float fracNegSampled,
                          const IntegralHogCascade& cascade);

  // Initializes the data for computing the approximate submodular cost.
  //
  // Inputs:
  // missCost - Cost of a miss
  // falsePosCost - Cost of a false positive
  // timeCostPerError - Conversion factor for 1s of processing time compared
  //                    to an error.
  // fracNegSampled - fraction of the number of negative examples we're
  //                  actually training with. So the number of negative
  //                  samples seen by this class is trueNegs/fracNegSampled
  // cascade - The cascade to approximate to
  //
  // Outputs:
  // removeCosts - Map for each stage for the change in cost if it is removed
  // pastCascade - Vector specifying which entries got past the cascade from
  //               rows in scores_
  void InitializeApproxSubCost(
    float missCost,
    float falsePosCost,
    float timeCostPerError,
    float fracNegSampled,
    const IntegralHogCascade& cascade,
    boost::unordered_map<IntegralHogCascade::Stage, float>* removeCosts,
    cv::Mat* pastCascade);

  float CalculateDeltaForRemovingStage(
    float falsePosCost,
    float timeCostPerError,
    float fracNegSampled,
    const IntegralHogCascade& cascade,
    const boost::unordered_map<IntegralHogCascade::Stage, float>& approxCosts,
    const IntegralHogCascade::Stage& stage);

  // Find the best approximate cost for a given detector.
  //
  // missCost, falsePosCost, timeCostPerError, fraceNegSampled -
  //   relative weights for the costs.
  // scores - score of the detector evaluated on each candidate region
  // usefulPassApproxCascade - usefule regions that pass the approx cascade
  // falsePosWins - False positive windows from the current cascade
  // windowsPassed - Windows that pass the current cascade
  // usefulWindows - Windows that are positives for training
  // notUsefuleWindows - Windows that are negatives for training
  //
  // Returns: Pair of <cost, bestThreshold>
  std::pair<float, float> GetBestApproxCost(
    unsigned int detectorIdx,
    float missCost,
    float falsePosCost,
    float timeCostPerError,
    float fracNegSampled,
    double baseHogTime,
    float nPassedApproxCascade,
    const cv::Mat& usefulPassApproxCascade,
    const cv::Mat& falsePosWins,
    const cv::Mat& windowsPassed,
    const cv::Mat& usefulWindows,
    const cv::Mat& notUsefulWindows);

};

// Class that finds the threshold to minimize the cost of a filter
class DeltaMinimizer {
public:
  DeltaMinimizer();
  ~DeltaMinimizer();

  // Minimizes the cost of adding a current detector
  //
  // Inputs:
  // missCost, falsePosCost, timeCostPerError, fraceNegSampled -
  //   relative weights for the costs.
  // scores - score of the detector evaluated on each candidate region
  // usefulPassApproxCascade - usefule regions that pass the approx cascade
  // falsePosWins - False positive windows from the current cascade
  // windowsPassed - Windows that pass the current cascade
  // usefulWindows - Windows that are positives for training
  // notUsefuleWindows - Windows that are negatives for training
  // timeCalculator - To calculate the time
  // cpuDelta - cpu cost to run this detector
  float MinimizeCost(float missCost,
                     float falsePosCost,
                     float timeCostPerError,
                     float fracNegSampled,
                     int nImages,
                     float trueImageSum,
                     double baseHogTime,
                     const cv::Mat_<float>& scores,
                     const cv::Mat& usefulPassApproxCascade,
                     const cv::Mat& falsePosWins,
                     const cv::Mat& windowsPassed,
                     const cv::Mat& usefulWindows,
                     const cv::Mat& notUsefulWindows,
                     const IntegralCascadeTimeCalculator* timeCalculator,
                     float cpuDelta);

  float bestCost() const { return bestCost_; }
  float bestThresh() const { return bestThresh_; }

  // Evaluates the cost function and its derivative with respect to
  // the number of entries filtered. Public because gsl is old and
  // needs acess to this.
  void EvalWithDeriv(const gsl_vector* nFiltered, void* params,
                     double* val, gsl_vector* derivs) const;
  double EvalCost(const gsl_vector* nFiltered, void* params) const;
  void EvalDeriv(const gsl_vector* nFiltered, void* params,
                 gsl_vector* derivs) const;

private:
  float bestCost_;
  float bestThresh_;

  // Used for the minimization
  gsl_multimin_fdfminimizer* minimizer_;
  gsl_multimin_function_fdf minFunc_;

  // Data used for the calculator
  double cpuDelta_;
  // Map from the number of new windows thresholded to the threshold.
  boost::scoped_ptr<Interpolator> thresholdInterpolator_;
  // Interpolator for the cost of extra misses with a given threshold
  boost::scoped_ptr<Interpolator> missCostInterpolator_;
  // Interpolator for the cost of the false positives relative to the
  // number of new windows thresholded.
  boost::scoped_ptr<Interpolator> falsePosInterpolator_;
  // Interpolator for the HOG time delta
  boost::scoped_ptr<Interpolator> hogTimeInterpolator_;

  void RunMinimization(double minWindowsThreshed, double maxWindowsThreshed,
                       double startPoint, double firstStep,
                       float* bestCost, float* bestThresh);

};

} // namespace

#endif //__HOG_DETECTOR_INTEGRAL_HOG_CASCADE_TRAINER_H__


//! \file openmc_driver.h
//! Driver to initialize and run OpenMC in stages
#ifndef ENRICO_OPENMC_DRIVER_H
#define ENRICO_OPENMC_DRIVER_H

#include "enrico/cell_instance.h"
#include "enrico/geom.h"
#include "enrico/neutronics_driver.h"

#include "openmc/cell.h"
#include "openmc/tallies/filter_cell_instance.h"
#include "openmc/tallies/tally.h"
#include <gsl/gsl>
#include <mpi.h>

#include <vector>

namespace enrico {

//! Driver to initialize and run OpenMC in stages
class OpenmcDriver : public NeutronicsDriver {
public:
  //! One-time initalization of OpenMC and member variables
  //! \param comm An existing MPI communicator used to inialize OpenMC
  explicit OpenmcDriver(MPI_Comm comm);

  //! One-time finalization of OpenMC
  ~OpenmcDriver();

  //////////////////////////////////////////////////////////////////////////////
  // NeutronicsDriver interface

  //! Find cells corresponding to a vector of positions
  //! \param positions (x,y,z) coordinates to search for
  //! \return Handles to cells
  std::vector<CellHandle> find(const std::vector<Position>& position) override;

  //! Set the density of the material in a cell
  //! \param cell Handle to a cell
  //! \param rho Density in [g/cm^3]
  void set_density(CellHandle cell, double rho) const override;

  //! Set the temperature of a cell
  //! \param cell Handle to a cell
  //! \param T Temperature in [K]
  void set_temperature(CellHandle cell, double T) const override;

  //! Get the density of a cell
  //! \param cell Handle to a cell
  //! \return Cell density in [g/cm^3]
  double get_density(CellHandle cell) const override;

  //! Get the temperature of a cell
  //! \param cell Handle to a cell
  //! \return Temperature in [K]
  double get_temperature(CellHandle cell) const override;

  //! Get the volume of a cell
  //! \param cell Handle to a cell
  //! \return Volume in [cm^3]
  double get_volume(CellHandle cell) const override;

  //! Detemrine whether a cell contains fissionable nuclides
  //! \param cell Handle to a cell
  //! \return Whether the cell contains fissionable nuclides
  bool is_fissionable(CellHandle cell) const override;

  std::size_t n_cells() const override { return cells_.size(); }

  //! Create energy production tallies
  void create_tallies() override;

  //! Determine number of cells participating in coupling
  //! \return Number of cells
  xt::xtensor<double, 1> heat_source(double power) const final;

  std::string cell_label(CellHandle cell) const;

  //////////////////////////////////////////////////////////////////////////////
  // Driver interface

  //! Initialization required in each Picard iteration
  void init_step() final;

  //! Runs OpenMC for one Picard iteration
  void solve_step() final;

  //! Writes OpenMC output for given timestep and iteration
  //! \param timestep timestep index
  //! \param iteration iteration index
  void write_step(int timestep, int iteration) final;

  //! Finalization required in each Picard iteration
  void finalize_step() final;

private:
  // Data members
  openmc::Tally* tally_;               //!< Fission energy deposition tally
  openmc::CellInstanceFilter* filter_; //!< Cell instance filter
  std::vector<CellInstance> cells_;    //!< Array of cell instances
  int n_fissionable_cells_;            //!< Number of fissionable cells in model
};

} // namespace enrico

#endif // ENRICO_OPENMC_DRIVER_H


/*******************************************************************************  *
*
*       This file is part of the General Hidden Markov Model Library,
*       GHMM version __VERSION__, see http://ghmm.org
*
*       Filename: ghmm/ghmm/root_finder.c
*       Authors:  Achim Gaedke
*
*       Copyright (C) 1998-2004 Alexander Schliep
*       Copyright (C) 1998-2001 ZAIK/ZPR, Universitaet zu Koeln
*       Copyright (C) 2002-2004 Max-Planck-Institut fuer Molekulare Genetik,
*                               Berlin
*
*       Contact: schliep@ghmm.org
*
*       This library is free software; you can redistribute it and/or
*       modify it under the terms of the GNU Library General Public
*       License as published by the Free Software Foundation; either
*       version 2 of the License, or (at your option) any later version.
*
*       This library is distributed in the hope that it will be useful,
*       but WITHOUT ANY WARRANTY; without even the implied warranty of
*       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*       Library General Public License for more details.
*
*       You should have received a copy of the GNU Library General Public
*       License along with this library; if not, write to the Free
*       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*       This file is version $Revision: 2267 $
*                       from $Date: 2009-04-24 11:01:58 -0400 (Fri, 24 Apr 2009) $
*             last change by $Author: grunau $.
*
*******************************************************************************/

#ifdef HAVE_CONFIG_H
#  include "../config.h"
#endif  /*  */

#ifndef DO_WITH_GSL

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "ghmm_internals.h"

double ghmm_zbrent_AB (double (*func) (double, double, double, double),
                   double x1, double x2, double tol, double A, double B,
                   double eps)
{
  fprintf (stderr, "Function ghmm_zbrent_AB() not implemented!\n");
  exit (1);

    /*
       double a, b, c;
       double fa, fb, fc;

       a = min(x1, x2);
       fa = (*func)(a);
       c = max(x1, x2);
       fc = (*func)(c);
       b = (c - a)/2.0;
       fb = (*func)(b);

       while (fabs(c - a) > tol + (tol * min(fabs(a), fabs(c))))
       {
       r = fb/fc;
       s = fb/fa;
       t = fa/fc;
       p = s * (t * (r - t) * (c - b) - (1.0 - r) * (b - a));
       q = (t - 1.0) * (r - 1.0) * (s - 1.0);

       x = b + p/q;

       if (x > a && x < c)
       {
       / * Accept interpolating point * /
       if (x < b)
       {
       c = b;
       fc = fb;
       b = x;
       fb = (*func)(b);
       }
       else if (x > b)
       {
       a = b;
       fa = fb;
       b = x;
       fb = (*func)(b);
       }
       }
       else
       {
       / * Use bisection * /
       }
       }
     */
}
#else   /*  */

#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>

/* struct for function pointer and parameters except 1st one */
  struct parameter_wrapper {
  double (*func) (double, double, double, double);
   double x2;
    double x3;
    double x4;
 } parameter;

/* calls the given function during gsl root solving iteration
   the first parameter variates, all other are kept constant
*/
double function_wrapper (double x, void *p)
{
  struct parameter_wrapper *param = (struct parameter_wrapper *) p;
  return param->func (x, param->x2, param->x3, param->x4);
}

/*
  this interface is used in sreestimate.c
 */
double ghmm_zbrent_AB (double (*func) (double, double, double, double), double x1,
                  double x2, double tol, double A, double B, double eps)
{

    /* initialisation of wrapper structure */
  struct parameter_wrapper param;
  gsl_function f;

#ifdef HAVE_GSL_INTERVAL /* gsl_interval vanished with version 0.9 */
    gsl_interval x;

#endif  /*  */
    gsl_root_fsolver * s;
  double tolerance;
  int success = 0;
  double result = 0;
  param.func = func;
  param.x2 = A;
  param.x3 = B;
  param.x4 = eps;
  f.function = &function_wrapper;
  f.params = (void *) &param;
  tolerance = tol;

    /* initialisation */
#ifdef HAVE_GSL_INTERVAL
# ifdef GSL_ROOT_FSLOVER_ALLOC_WITH_ONE_ARG
    x.lower = x1;
  x.upper = x2;
  s = gsl_root_fsolver_alloc (gsl_root_fsolver_brent);
  gsl_root_fsolver_set (s, &f, x);

# else
    s = gsl_root_fsolver_alloc (gsl_root_fsolver_brent, &f, x);

# endif
#else   /* gsl_interval vanished with version 0.9 */
    s = gsl_root_fsolver_alloc (gsl_root_fsolver_brent);
  gsl_root_fsolver_set (s, &f, x1, x2);

#endif  /*  */

    /* iteration */
    do {
    success = gsl_root_fsolver_iterate (s);
    if (success == GSL_SUCCESS)
       {

#ifdef HAVE_GSL_INTERVAL
        gsl_interval new_x;
      new_x = gsl_root_fsolver_interval (s);
      success = gsl_root_test_interval (new_x, tolerance, tolerance);

#else   /* gsl_interval vanished with version 0.9 */
      double x_up;
      double x_low;
      (void) gsl_root_fsolver_iterate (s);
      x_up = gsl_root_fsolver_x_upper (s);
      x_low = gsl_root_fsolver_x_lower (s);
      success = gsl_root_test_interval (x_low, x_up, tolerance, tolerance);

#endif  /*  */
      }
  } while (success == GSL_CONTINUE);

    /* result */
    if (success != GSL_SUCCESS)
     {
    gsl_error ("solver failed", __FILE__, __LINE__, success);
    }

  else
     {
    result = gsl_root_fsolver_root (s);
    }

    /* destruction */
    gsl_root_fsolver_free (s);
  return result;
}

#endif  /*  */


/* Author: Edward Hutter */

#ifndef SHARED
#define SHARED

// System includes
#include <fstream>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <complex>
#include <vector>
#include <map>
#include <algorithm>
#include <utility>
#include <tuple>
#include <cmath>
#include <string>
#include <assert.h>
#include <complex>

#include <mpi.h>
#include "mkl.h"

#ifdef CRITTER
#include "critter.h"
#else
//#ifdef PORTER
//#include <cblas.h>
//#include "/home/hutter2/hutter2/external/BLAS/OpenBLAS/lapack-netlib/LAPACKE/include/lapacke.h"
//#endif
#define CRITTER_START(ARG)
#define CRITTER_STOP(ARG)
#endif

template<typename ScalarType>
class mpi_type;

template<>
class mpi_type<float>{
public:
  constexpr static size_t type = MPI_FLOAT;
};
template<>
class mpi_type<double>{
public:
  constexpr static size_t type = MPI_DOUBLE;
};

#endif /*SHARED*/


/*
 *  gslrandomgen.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 *  Interface to GSL Random Number Generators
 *
 */

#ifndef GSLRANDOMGEN_H
#define GSLRANDOMGEN_H

// C++ includes:
#include <cassert>
#include <list>
#include <string>

// Generated includes:
#include "config.h"

// Includes from librandom:
#include "random_datums.h"
#include "randomgen.h"

// Includes from sli:
#include "dictdatum.h"

// essential GSL includes or replacements
// GSL Versions < 1.2 have weak MT seeding
#ifdef HAVE_GSL

// "Real" version in presence of GSL

// External includes:
#include <gsl/gsl_rng.h>

namespace librandom
{

/**
 * class GslRandomGen
 * C++ wrapper for GSL/GSL-style generators.
 * @note
 * This class should only be used within librandom.
 *
 * @ingroup RandomNumberGenerators
 */

class GslRandomGen : public RandomGen
{
  friend class GSL_BinomialRandomDev;

public:
  explicit GslRandomGen( const gsl_rng_type*, //!< given RNG, given seed
    unsigned long );

  ~GslRandomGen();

  //! Add all GSL RNGs to rngdict
  static void add_gsl_rngs( Dictionary& );

  RngPtr
  clone( unsigned long s )
  {
    return RngPtr( new GslRandomGen( rng_type_, s ) );
  }

private:
  void seed_( unsigned long );
  double drand_( void );

private:
  gsl_rng_type const* rng_type_;
  gsl_rng* rng_;
};

inline void
GslRandomGen::seed_( unsigned long s )
{
  gsl_rng_set( rng_, s );
}

inline double
GslRandomGen::drand_( void )
{
  return gsl_rng_uniform( rng_ );
}

//! Factory class for GSL-based random generators
class GslRNGFactory : public GenericRNGFactory
{
public:
  GslRNGFactory( gsl_rng_type const* const );
  RngPtr create( unsigned long ) const;

private:
  //! GSL generator type information
  gsl_rng_type const* const gsl_rng_;
};
}

#else

// NO GSL Available---Implement class as empty shell
namespace librandom
{

class GslRandomGen : public RandomGen
{
public:
  //! Add all GSL RNGs to rngdict
  //! Do nothing if GSL not available
  static void
  add_gsl_rngs( Dictionary& )
  {
  }

private:
  GslRandomGen()
  {
    assert( false );
  }
  ~GslRandomGen()
  {
    assert( false );
  }
};
}

#endif

#endif


#ifndef LIBTENSOR_CBLAS_H_H
#define LIBTENSOR_CBLAS_H_H

#ifdef USE_CBLAS
extern "C" { // Fixes older cblas.h versions without extern "C"
#include <cblas.h>
}
#endif // USE_CBLAS

#ifdef USE_GSL
#include <gsl/gsl_cblas.h>
#endif // USE_GSL

#endif // LIBTENSOR_CBLAS_H_H


#ifndef _EM_LA_H_
#define _EM_LA_H_ 1

#include <petsc.h>

struct EMContext;

PetscErrorCode access_vec(Vec, std::vector<PetscInt> &, int, double *);

PetscErrorCode setup_ams(EMContext *);
PetscErrorCode destroy_ams(EMContext *);

PetscErrorCode create_pc(EMContext *);
PetscErrorCode destroy_pc(EMContext *);
PetscErrorCode pc_apply_b(PC, Vec, Vec);

PetscErrorCode matshell_createvecs_a(Mat, Vec *, Vec *);
PetscErrorCode matshell_mult_a(Mat, Vec, Vec);

PetscErrorCode solve_linear_system(EMContext *, const PETScBlockVector &, PETScBlockVector &, PetscInt, PetscReal);

#endif


/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (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.openairinterface.org/?page_id=698
 *
 * 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.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

#include <string.h>
#include <math.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <cblas.h>

#include "SIMULATION/TOOLS/defs.h"
#include "SIMULATION/RF/defs.h"
#include "PHY/types.h"
#include "PHY/defs.h"
#include "PHY/extern.h"
#include "oaisim_config.h"

#ifdef OPENAIR2
#include "LAYER2/MAC/defs.h"
#include "LAYER2/MAC/extern.h"
#include "UTIL/LOG/log_if.h"
#include "UTIL/LOG/log_extern.h"
#include "RRC/LITE/extern.h"
#include "PHY_INTERFACE/extern.h"
#include "UTIL/OCG/OCG.h"
#include "UTIL/OMG/omg.h"
#include "UTIL/OPT/opt.h" // to test OPT
#endif

#include "SCHED/defs.h"
#include "SCHED/extern.h"

#include "oaisim.h"

#define PI 3.1416
#define Am 20

#define MCL (-70) /*minimum coupling loss (MCL) in dB*/
//double sinr[NUMBER_OF_eNB_MAX][2*25];
/*
extern double sinr_bler_map[MCS_COUNT][2][16];
extern double sinr_bler_map_up[MCS_COUNT][2][16];
double SINRpost_eff[301];
extern double MI_map_4qam[3][162];
extern double MI_map_16qam[3][197];
extern double MI_map_64qam[3][227];
*/
// Extract the positions of UE and ENB from the mobility model

void extract_position (node_list* input_node_list, node_desc_t **node_data, int nb_nodes)
{

  int i;

  for (i=0; i<nb_nodes; i++) {
    if ((input_node_list != NULL) &&  (node_data[i] != NULL)) {

      node_data[i]->x = input_node_list->node->x_pos;

      if (node_data[i]->x <0.0)
        node_data[i]->x = 0.0;

      node_data[i]->y = input_node_list->node->y_pos;

      if (node_data[i]->y <0.0)
        node_data[i]->y = 0.0;

      LOG_D(OCM, "extract_position: added node_data %d with position X: %f and Y: %f \n", i,input_node_list->node->x_pos, input_node_list->node->y_pos );
      input_node_list = input_node_list->next;
    } else {
      LOG_E(OCM, "extract_position: Null pointer!!!\n");
      //exit(-1);
    }
  }
}
void extract_position_fixed_enb  (node_desc_t **node_data, int nb_nodes, frame_t frame)
{
  int i;

  for (i=0; i<nb_nodes; i++) {
    if (i==0) {
      node_data[i]->x = 0;
      node_data[i]->y = 500;
    } else if (i == 1 ) {
      node_data[i]->x = 866;//
      node_data[i]->y = 1000;
    } else if (i == 2 ) {
      node_data[i]->x = 866;
      node_data[i]->y = 0;
    }
  }
}

void extract_position_fixed_ue  (node_desc_t **node_data, int nb_nodes, frame_t frame)
{
  int i;

  if(frame<50)
    for (i=0; i<nb_nodes; i++) {
      if (i==0) {
        node_data[i]->x = 2050;
        node_data[i]->y = 1500;
      } else {
        node_data[i]->x = 2150;
        node_data[i]->y = 1500;
      }
    }
  else {
    for (i=0; i<nb_nodes; i++) {
      if (i==0) {
        node_data[i]->x = 1856 - (frame - 49);
        // if(node_data[i]->x > 2106)
        //  node_data[i]->x = 2106;
        node_data[i]->y = 1813 + (frame - 49);
        // if(node_data[i]->y < 1563)
        //  node_data[i]->y = 1563;
        // if( node_data[i]->x == 2106)
        //   node_data[i]->x = 2106 - (frame - 49);
      } else {
        node_data[i]->x = 2106 - (frame - 49);
        // if(node_data[i]->x < 1856)
        //  node_data[i]->x = 1856;
        node_data[i]->y = 1563 + (frame - 49);
        // if(node_data[i]->y < 1813)
        //  node_data[i]->y = 1813;
      }
    }
  }

}

void init_ue(node_desc_t  *ue_data, UE_Antenna ue_ant)  //changed from node_struct
{

  ue_data->n_sectors = 1;
  ue_data->phi_rad = 2 * PI;
  ue_data->ant_gain_dBi = ue_ant.antenna_gain_dBi;
  ue_data->tx_power_dBm = ue_ant.tx_power_dBm;
  ue_data->rx_noise_level = ue_ant.rx_noise_level_dB; //value in db

}

void init_enb(node_desc_t  *enb_data, eNB_Antenna enb_ant)  //changed from node_struct
{

  int i;
  double sect_angle[3]= {0,2*PI/3,4*PI/3};

  enb_data->n_sectors = enb_ant.number_of_sectors;

  for (i=0; i<enb_data->n_sectors; i++)
    enb_data->alpha_rad[i] = sect_angle[i]; //enb_ant.alpha_rad[i];

  enb_data->phi_rad = enb_ant.beam_width_dB;
  enb_data->ant_gain_dBi = enb_ant.antenna_gain_dBi;
  enb_data->tx_power_dBm = enb_ant.tx_power_dBm;
  enb_data->rx_noise_level = enb_ant.rx_noise_level_dB;

}

void calc_path_loss(node_desc_t* enb_data, node_desc_t* ue_data, channel_desc_t *ch_desc, Environment_System_Config env_desc, double **Shad_Fad)
{

  double dist;
  double path_loss;
  double gain_max;
  double gain_sec[3];
  double alpha, theta;

  int count;

  dist = sqrt(pow((enb_data->x - ue_data->x), 2) + pow((enb_data->y - ue_data->y), 2));

  path_loss = env_desc.fading.free_space_model_parameters.pathloss_0_dB -
              10*env_desc.fading.free_space_model_parameters.pathloss_exponent * log10(dist/1000);
  LOG_D(OCM,"dist %f, Path loss %f\n",dist,ch_desc->path_loss_dB);

  /* Calculating the angle in the range -pi to pi from the slope */
  alpha = atan2((ue_data->y - enb_data->y),(ue_data->x - enb_data->x));

  if (alpha < 0)
    alpha += 2*PI;

  //printf("angle in radians is %lf\n", ue_data[UE_id]->alpha_rad[eNB_id]);
  ch_desc->aoa = alpha;
  ch_desc->random_aoa = 0;

  if (enb_data->n_sectors==1) //assume omnidirectional antenna
    gain_max = 0;
  else {
    gain_max = -1000;

    for(count = 0; count < enb_data->n_sectors; count++) {
      theta = enb_data->alpha_rad[count] - alpha;
      /* gain = -min(Am , 12 * (theta/theta_3dB)^2) */
      gain_sec[count] = -(Am < (12 * pow((theta/enb_data->phi_rad),2)) ? Am : (12 * pow((theta/enb_data->phi_rad),2)));

      if (gain_sec[count]>gain_max)  //take the sector that we are closest too (or where the gain is maximum)
        gain_max = gain_sec[count];
    }
  }

  path_loss += enb_data->ant_gain_dBi + gain_max + ue_data->ant_gain_dBi;

  if (Shad_Fad!=NULL)
    path_loss += Shad_Fad[(int)ue_data->x][(int)ue_data->y];

  ch_desc->path_loss_dB = MCL < path_loss ?  MCL : path_loss;
  //LOG_D(OCM,"x_coordinate\t%f\t,y_coordinate\t%f\t, path_loss %f\n",ue_data->x,ue_data->y,ch_desc->path_loss_dB);
}

void init_snr(channel_desc_t* eNB2UE, node_desc_t *enb_data, node_desc_t *ue_data, double* sinr_dB, double* N0, uint8_t transmission_mode, uint16_t q, uint8_t dl_power_off, uint16_t nb_rb)
{

  double thermal_noise,abs_channel,channelx, channely,channelx_i, channely_i ;
  int count;
  int aarx,aatx;
  uint8_t qq;

  /* Thermal noise is calculated using 10log10(K*T*B) K = Boltzmann's constant T = room temperature B = bandwidth*/
  thermal_noise = -174 + 10*log10(15000); //per RE; value in dBm

  //for (aarx=0; aarx<eNB2UE->nb_rx; aarx++)
  *N0 = thermal_noise + ue_data->rx_noise_level;

  LOG_D(OCM,"Path loss %lf, noise (N0) %lf, signal %lf, snr %lf\n",
        eNB2UE->path_loss_dB,
        thermal_noise + ue_data->rx_noise_level,
        enb_data->tx_power_dBm + eNB2UE->path_loss_dB,
        enb_data->tx_power_dBm + eNB2UE->path_loss_dB - (thermal_noise + ue_data->rx_noise_level));

  if(transmission_mode == 5 && dl_power_off==1)
    transmission_mode = 6;

  switch(transmission_mode) {
  case 1:

    //printf ("coupling factor is %lf\n", coupling);
    for (count = 0; count < (12 * nb_rb); count++) {
      sinr_dB[count] = enb_data->tx_power_dBm
                       + eNB2UE->path_loss_dB
                       - (thermal_noise + ue_data->rx_noise_level)
                       + 10 * log10 (pow(eNB2UE->chF[0][count].x, 2)
                                     + pow(eNB2UE->chF[0][count].y, 2));
      //printf("sinr_dB[%d]: %1f\n",count,sinr_dB[count]);
      //printf("Dl_link SNR for res. block %d is %lf\n", count, sinr[eNB_id][count]);
    }

    break;

  case 2:

    for (count = 0; count < (12 * nb_rb); count++) {
      abs_channel=0;

      for (aarx=0; aarx<eNB2UE->nb_rx; aarx++) {
        for (aatx=0; aatx<eNB2UE->nb_tx; aatx++) {
          abs_channel += (pow(eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x, 2) + pow(eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y, 2));
        }
      }

      sinr_dB[count] = enb_data->tx_power_dBm
                       + eNB2UE->path_loss_dB
                       - (thermal_noise + ue_data->rx_noise_level)
                       + 10 * log10 (abs_channel/2);
      // printf("sinr_dB[%d]: %1f\n",count,sinr_dB[count]);
    }

    break;

  case 5:
    for (count = 0; count < (12 * nb_rb); count++) {
      channelx=0;
      channely=0;
      channelx_i=0;
      channely_i=0;
      qq = (q>>(((count/12)>>2)<<1))&3;
      //printf("pmi_alloc %d: rb %d, pmi %d\n",q,count/12,qq);

      //      qq = q;
      for (aarx=0; aarx<eNB2UE->nb_rx; aarx++) {
        for (aatx=0; aatx<eNB2UE->nb_tx; aatx++) {
          switch(qq) {
          case 0:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channelx_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channelx_i -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely_i -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            }

            break;

          case 1:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channelx_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channelx_i += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely_i += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            }

            break;

          case 2:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channelx_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channely += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channelx_i += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channely_i -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;

            }

            break;

          case 3:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channelx_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely_i = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channely -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channelx_i -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channely_i += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
            }

            break;

          default:
            LOG_E(EMU,"Problem in SINR Calculation for TM5 \n");
            break;

          }//switch(q)

        }//aatx
      }//aarx

      /*  sinr_dB[count] = enb_data->tx_power_dBm
         + eNB2UE->path_loss_dB
        - (thermal_noise + ue_data->rx_noise_level)
        + 10 * log10 ((pow(channelx,2) + pow(channely,2))/2) - 10 * log10 ((pow(channelx_i,2) + pow(channely_i,2))/2);
      */
      sinr_dB[count] = enb_data->tx_power_dBm
                       + eNB2UE->path_loss_dB
                       - (thermal_noise + ue_data->rx_noise_level)
                       + 10 * log10 ((pow(channelx,2) + pow(channely,2))) - 10 * log10 ((pow(channelx_i,2) + pow(channely_i,2))) - 3; // 3dB is subtracted as the tx_power_dBm is to be adjusted on per user basis
      // printf("sinr_dB[%d]: %1f\n",count,sinr_dB[count]);
    }

    break;

  case 6:
    for (count = 0; count < (12 * nb_rb); count++) {
      channelx=0;
      channely=0;
      qq = (q>>(((count/12)>>2)<<1))&3;
      //printf("pmi_alloc %d: rb %d, pmi %d\n",q,count/12,qq);

      //      qq = q;
      for (aarx=0; aarx<eNB2UE->nb_rx; aarx++) {
        for (aatx=0; aatx<eNB2UE->nb_tx; aatx++) {
          switch(qq) {
          case 0:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            }

            break;

          case 1:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            }

            break;

          case 2:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channely += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
            }

            break;

          case 3:
            if (channelx==0 || channely==0) {
              channelx = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
              channely = eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
            } else {
              channelx += eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].y;
              channely -= eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][count].x;
            }

            break;

          default:
            LOG_E(EMU,"Problem in SINR Calculation for TM6 \n");
            break;

          }//switch(q)

        }//aatx
      }//aarx

      sinr_dB[count] = enb_data->tx_power_dBm
                       + eNB2UE->path_loss_dB
                       - (thermal_noise + ue_data->rx_noise_level)
                       + 10 * log10 ((pow(channelx,2) + pow(channely,2))/2);

      // printf("sinr_dB[%d]: %1f\n",count,sinr_dB[count]);
    }

    break;

  default:
    LOG_E(EMU,"Problem in SINR Initialization in sinr_sim.c\n");
    break;
  }//switch
}//function ends

void calculate_sinr(channel_desc_t* eNB2UE, node_desc_t *enb_data, node_desc_t *ue_data, double *sinr_dB, uint16_t nb_rb)
{

  double sir, thermal_noise;
  short count;

  /* Thermal noise is calculated using 10log10(K*T*B) K = Boltzmann's constant T = room temperature B = bandwidth */
  thermal_noise = -174 + 10*log10(15000); //per RE, value in dBm

  for (count = 0; count < 12 * nb_rb; count++) {
    sir = enb_data->tx_power_dBm
          + eNB2UE->path_loss_dB
          - (thermal_noise + ue_data->rx_noise_level)
          + 10 * log10 (pow(eNB2UE->chF[0][count].x, 2)
                        + pow(eNB2UE->chF[0][count].y, 2));

    if (sir > 0)
      sinr_dB[count] -= sir;

    //printf("*****sinr% lf \n",sinr_dB[count]);
  }
}
void get_beta_map()
{
  char *file_path = NULL;
  //int table_len = 0;
  int t,u;
  int mcs = 0;
  char *sinr_bler;
  char buffer[1000];
  FILE *fp;
  double perf_array[13];

  file_path = (char*) malloc(512);

  for (mcs = 0; mcs < MCS_COUNT; mcs++) {
    snprintf( file_path, 512, "%s/SIMULATION/LTE_PHY/BLER_SIMULATIONS/AWGN/AWGN_results/bler_tx1_chan18_nrx1_mcs%d.csv", getenv("OPENAIR1_DIR"), mcs );
    fp = fopen(file_path,"r");

    if (fp == NULL) {
      LOG_E(OCM,"ERROR: Unable to open the file %s! Exitng.\n", file_path);
      exit(-1);
    }

    // else {
    if (fgets (buffer, 1000, fp) != NULL) {
      if (fgets (buffer, 1000, fp) != NULL) {
        table_length[mcs] = 0;

        while (!feof (fp)) {
          u = 0;
          sinr_bler = strtok (buffer, ";");

          while (sinr_bler != NULL) {
            perf_array[u] = atof (sinr_bler);
            u++;
            sinr_bler = strtok (NULL, ";");
          }

          if ((perf_array[4] / perf_array[5]) < 1) {
            sinr_bler_map[mcs][0][table_length[mcs]] = perf_array[0];
            sinr_bler_map[mcs][1][table_length[mcs]] = (perf_array[4] / perf_array[5]);
            table_length[mcs]++;

            if (table_length[mcs]>MCS_TABLE_LENGTH_MAX) {
              LOG_E(OCM,"Error reading MCS table. Increase MCS_TABLE_LENGTH_MAX (mcs %d)!\n",mcs);
              exit(-1);
            }
          }

          if (fgets (buffer, 1000, fp) != NULL) {
          }
        }
      }
    }

    fclose(fp);
    //   }
    LOG_D(OCM,"Print the table for mcs %d\n",mcs);

    for (t = 0; t<table_length[mcs]; t++)
      LOG_D(OCM,"%lf  %lf \n ",sinr_bler_map[mcs][0][t],sinr_bler_map[mcs][1][t]);
  }

  free(file_path);
}

//this function reads and stores the Mutual information tables for the MIESM abstraction.
void get_MIESM_param()
{
  char *file_path = NULL;
  char buffer[10000];
  FILE *fp;
  int qam[3] = {4,16,64};
  int q,cnt;
  char *result = NULL;
  int table_len=0;
  int t;
  file_path = (char*) malloc(512);

  for (q=0; q<3; q++) {
    sprintf(file_path,"%s/SIMU/USER/files/MI_%dqam.csv",getenv("OPENAIR_TARGETS"),qam[q]);
    fp = fopen(file_path,"r");

    if (fp == NULL) {
      printf("ERROR: Unable to open the file %s\n", file_path);
      exit(-1);
    } else {
      cnt=-1;

      switch(qam[q]) {
      case 4:
        while (!feof(fp)) {
          table_len =0;
          cnt++;

          if (fgets(buffer, 10000, fp) != NULL) {
            result = strtok (buffer, ",");

            while (result != NULL) {
              MI_map_4qam[cnt][table_len] = atof (result);
              result = strtok (NULL, ",");
              table_len++;
            }
          }
        }

        for (t = 0; t < 162; t++) {
          // MI_map_4Qam[0][t] = pow(10,0.1*(MI_map_4Qam[0][t]));
          LOG_D(OCM, "MIESM 4QAM Table: %lf  %lf  %1f\n ",MI_map_4qam[0][t],MI_map_4qam[1][t], MI_map_4qam[2][t]);
          // printf("MIESM 4QAM Table: %lf  %lf  %1f\n ",MI_map_4qam[0][t],MI_map_4qam[1][t], MI_map_4qam[2][t]);
        }

        break;

      case 16:
        while (!feof(fp)) {
          table_len =0;
          cnt++;

          if (fgets (buffer, 10000, fp) != NULL) {
            result = strtok (buffer, ",");

            while (result != NULL) {
              MI_map_16qam[cnt][table_len] = atof (result);
              result = strtok (NULL, ",");
              table_len++;
            }
          }
        }

        for (t = 0; t < 197; t++) {
          // MI_map_16Qam[0][t] = pow(10,0.1*(MI_map_16Qam[0][t]));
          LOG_D(OCM, "MIESM 16 QAM Table: %lf  %lf  %1f\n ",MI_map_16qam[0][t],MI_map_16qam[1][t], MI_map_16qam[2][t]);
          // printf("MIESM 16 QAM Table: %lf  %lf  %1f\n ",MI_map_16qam[0][t],MI_map_16qam[1][t], MI_map_16qam[2][t]);
        }

        break;

      case 64:
        while (!feof(fp)) {
          table_len=0;
          cnt++;

          if(cnt==3)
            break;

          if (fgets (buffer, 10000, fp) != NULL) {
            result = strtok(buffer, ",");

            while (result != NULL) {
              MI_map_64qam[cnt][table_len]= atof(result);
              result = strtok(NULL, ",");
              table_len++;
            }
          }
        }

        for (t = 0; t < 227; t++) {
          //MI_map_64Qam[0][t] = pow(10,0.1*(MI_map_64Qam[0][t]));
          LOG_D(OCM, "MIESM 64QAM Table: %lf  %lf  %1f\n ",MI_map_64qam[0][t],MI_map_64qam[1][t], MI_map_64qam[2][t]);
          // printf("MIESM 64QAM Table: %lf  %lf  %1f\n ",MI_map_64qam[0][t],MI_map_64qam[1][t], MI_map_64qam[2][t]);
        }

        break;

      default:
        LOG_E(EMU,"Error, bad input, quitting\n");
        break;
      }

    }

    fclose(fp);
  }

  free(file_path);
}


/*
GENETIC - A simple genetic algorithm.

Copyright 2014, Javier Burguete Tolosa.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
	list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY Javier Burguete Tolosa ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL Javier Burguete Tolosa OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * \file population.c
 * \brief Header file to define the population functions.
 * \author Javier Burguete Tolosa.
 * \copyright Copyright 2014 Javier Burguete Tolosa. All rights reserved.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <gsl/gsl_rng.h>
#include <glib.h>
#include "bits.h"
#include "entity.h"
#include "population.h"

/**
 * Function to init a population.
 *
 * \return 1 on succes, 0 on error.
 */
int
population_new (Population * population,        ///< Population.
                GeneticVariable * variable,     ///< Variables data.
                unsigned int nvariables,        ///< Number of variables.
                unsigned int genome_nbits,
                ///< Number of bits of each entity genome.
                unsigned int nentities, ///< Number of entities.
                double mutation_ratio,  ///< Mutation ratio.
                double reproduction_ratio,      ///< Reproduction ratio.
                double adaptation_ratio,        ///< Adaptation ratio.
                double threshold)
                ///< Threshold to finish the simulations.
{
  unsigned int i, nmutations, nreproductions, nadaptations;
  nmutations = mutation_ratio * nentities;
  nreproductions = reproduction_ratio * nentities;
  nadaptations = adaptation_ratio * nentities;
  i = nmutations + nreproductions + nadaptations;
  if (!i)
    {
      fprintf (stderr, "ERROR: no evolution\n");
      return 0;
    }
  if (i >= nentities)
    {
      fprintf (stderr, "ERROR: no survival of entities\n");
      return 0;
    }
  population->nsurvival = nentities - i;
  if (population->nsurvival < 2)
    {
      fprintf (stderr, "ERROR: unable to reproduce the entities\n");
      return 0;
    }
  population->variable = variable;
  population->nvariables = nvariables;
  population->nentities = nentities;
  population->mutation_max = nentities;
  population->mutation_min = population->reproduction_max
    = nentities - nmutations;
  population->reproduction_min = population->adaptation_max
    = population->reproduction_max - nreproductions;
  population->adaptation_min = population->nsurvival;
  population->genome_nbits = genome_nbits;
  population->genome_nbytes = bit_sizeof (genome_nbits);
  population->objective
		= (double *) g_slice_alloc (nentities * sizeof (double));
  population->entity = (Entity *) g_slice_alloc (nentities * sizeof (Entity));
  for (i = 0; i < population->nentities; ++i)
    {
      entity_new (population->entity + i, population->genome_nbytes, i);
      population->objective[i] = G_MAXDOUBLE;
    }
  population->threshold = threshold;
  population->stop = 0;
  return 1;
}

/**
 * Function to free the memory allocated in a population.
 */
void
population_init_genomes (Population * population,       ///< Population.
                         gsl_rng * rng) ///< GSL random numbers generator.
{
  unsigned int i;
  for (i = 0; i < population->nentities; ++i)
    entity_init (population->entity + i, rng);
}

/**
 * Function to free the memory allocated in a population.
 */
void
population_free (Population * population)       ///< Population.
{
  unsigned int i, nentities;
	nentities = population->nentities;
  for (i = 0; i < nentities; ++i)
    entity_free (population->entity + i);
  g_slice_free1 (nentities * sizeof (Entity), population->entity);
  g_slice_free1 (nentities * sizeof (double), population->objective);
}


/*
 * C version of Diffusive Nested Sampling (DNest4) by Brendon J. Brewer
 *
 * Yan-Rong Li, liyanrong@mail.ihep.ac.cn
 * Jun 30, 2016
 *
 */

#ifndef _MODEL2_H
#define _MODEL2_H

#include <stdbool.h>
#include <gsl/gsl_rng.h>

/*  data type */
typedef struct
{
  double x;
  double y;
}DataType;

/* number of model parameters */
extern int num_params;

/* data storage */
extern int num_data_points;
extern DataType *data;

extern int which_particle_update; // which particule to be updated
extern int which_level_update;
extern double *limits;
extern int thistask, totaltask;

extern DNestFptrSet *fptrset_thismodel2;

/* functions */
void from_prior_thismodel2(void *model);
void data_load_thismodel2();
void print_particle_thismodel2(FILE *fp, const void *model);
double log_likelihoods_cal_thismodel2(const void *model);
double perturb_thismodel2(void *model);
void restart_action_model2(int iflag);

#endif


#pragma once

#include <gsl\gsl>
#include <winrt\base.h>
#include <d3d11.h>
#include "DrawableGameComponent.h"
#include "DynamicLODMaterial.h"
#include "MatrixHelper.h"

namespace Rendering
{
	class DynamicLODDemo final : public Library::DrawableGameComponent
	{
	public:
		DynamicLODDemo(Library::Game& game, const std::shared_ptr<Library::Camera>& camera);
		DynamicLODDemo(const DynamicLODDemo&) = delete;
		DynamicLODDemo(DynamicLODDemo&&) = default;
		DynamicLODDemo& operator=(const DynamicLODDemo&) = default;
		DynamicLODDemo& operator=(DynamicLODDemo&&) = default;
		~DynamicLODDemo();

		int MaxTessellationFactor() const;
		void SetMaxTessellationFactor(int maxTessellationFactor);

		DirectX::XMFLOAT2 TessellationDistances() const;
		void SetTessellationDistances(DirectX::XMFLOAT2 tessellationDistances);
		void SetTessellationDistances(float minTessellationDistance, float maxTessellationDistance);

		virtual void Initialize() override;
		virtual void Draw(const Library::GameTime& gameTime) override;

	private:
		DynamicLODMaterial mMaterial;
		DirectX::XMFLOAT4X4 mWorldMatrix{ Library::MatrixHelper::Identity };
		winrt::com_ptr<ID3D11Buffer> mVertexBuffer;
		winrt::com_ptr<ID3D11Buffer> mIndexBuffer;
		std::size_t mIndexCount{ 0 };
		bool mUpdateMaterial{ true };
	};
}


/* rng/rng-dump.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_rng.h>

int
main (int argc, char **argv)
{
  int i, j ;
  char buffer[1024 * 4] ;
  gsl_rng * r ;

  gsl_rng_env_setup () ;

  r = gsl_rng_alloc (gsl_rng_default) ;

  if (argc != 1)
    {
      printf ("Usage: diehard\n");
      printf ("Output 3 million numbers in binary format,"
              "suitable for testing with DIEHARD\n");
      exit (0);
    }

  argv = 0 ; /* prevent warning about unused argument */

  for (i = 0; i < 3000 ; i++)
    {
      int status ;

      for (j = 0; j < 1024; j++)
        {
          unsigned long int u = gsl_rng_get (r) ;
          buffer[4 * j + 0] = u & 0xFF ;
          u >>= 8;
          buffer[4 * j + 1] = u & 0xFF ;
          u >>= 8;
          buffer[4 * j + 2] = u & 0xFF ;
          u >>= 8;
          buffer[4 * j + 3] = u & 0xFF ;

        }

      status = fwrite(buffer, 4 * sizeof(char), 1024, stdout) ;

      if (status != 1024)
        {
          perror("fwrite") ;
          exit(EXIT_FAILURE) ;
        }
    }

  return 0;
}