/**
 */
#include <time.h>
#include <math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_sort_vector.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_sort_double.h>
#include "inout_aper.h"
#include "aXe_grism.h"
#include "spce_sect.h"
#include "spc_back.h"
#include "spce_PET.h"
#include "spc_wl_calib.h"
#include "aXe_errors.h"
#include "fringe_conf.h"
#include "spc_resp.h"
#include "spce_pathlength.h"
#include "aper_conf.h"
#include "specmodel_utils.h"
#include "model_utils.h"
#include "spc_fluxcube.h"
#include "fringe_conf.h"
#include "dirimage_model.h"

#define MAX(x,y) (((x)>(y))?(x):(y))
#define MIN(x,y) (((x)<(y))?(x):(y))
#define SQR(x) ((x)*(x))

int
compute_dirimage_model(char dirim_file[], char conf_file[], char tpass_file[],
		       char specmod_file[], char objmod_file[], char aper_file[],
		       const double model_scale, const double tel_area, const double lambda_psf,
		       observation *obs, char map_file[])
{
  object          **oblist;
  dirobject       **dirlist;
  spectral_models *spec_mod;
  object_models   *obj_mod;

  interpolator    *tpass;

  gsl_matrix *dirimage_matrix=NULL;

  px_point npixels;

  // load the object list
  fprintf (stdout, "aXe_DIRIMAGE: Loading object aperture list...");
  oblist = file_to_object_list_seq (aper_file, obs);
  fprintf (stdout,"%d objects loaded.\n",object_list_size(oblist));

  // check whether highres models
  // are given
  if (strlen(specmod_file) > 0)
    // load the spectral models
    spec_mod = load_spectral_models(specmod_file);
  else
    // or set the struct to NULL
    spec_mod = NULL;

  // check whether direct emission models
  // are given
  if (strlen(objmod_file) > 0)
    obj_mod = load_object_models(objmod_file);
  else
    obj_mod = NULL;

  // get the sensitivity curve of the total passband
  tpass =  get_filter_sensitivity(tpass_file, tel_area);

  // get the image dimensions
  npixels = get_npixel(obs);

  // create the list of direct objects to be simulated
  // the interpolation type is fixed to linear
  dirlist = oblist_to_dirlist2(dirim_file, conf_file, npixels, oblist,
			       spec_mod, obj_mod, model_scale, 1);

  // determine the XOFF and YOFF values
  // for the various beams
  fill_xy_offsets(dirlist, conf_file);

  // create and fill the pixel matrix for the direct images
  dirimage_matrix = make_dirimage(oblist, dirlist, npixels, lambda_psf, tpass);

  // store the contamination image
  gsl_to_FITSimage (dirimage_matrix, map_file, 1, NULL);

  // check if memory must be released;
  // do it if necessary
  if (spec_mod != NULL)
    free_spectral_models(spec_mod);
  if (obj_mod != NULL)
    free_object_models(obj_mod);
  if (oblist !=NULL)
    free_oblist (oblist);

  // always release these
  // objects
  free_dirlist(dirlist);
  gsl_matrix_free(dirimage_matrix);
  free_interp(tpass);

  // return always '1'
  return 1;
}

gsl_matrix *
make_dirimage(object **oblist, dirobject **dirlist, const px_point npixels,
	      const double lambda_psf, interpolator *tpass)
{
  dirobject       *actdir;
  beam             actbeam;

  gsl_matrix      *dirimage_matrix;

  double cps        = 0.0;
  double sval       = 0.0;
  double value      = 0.0;

  int ii=0;
  int nx=0;
  int ny=0;

  d_point dpixel;

   // allocate memory for the image matrix
  dirimage_matrix = gsl_matrix_alloc(npixels.x, npixels.y);
  gsl_matrix_set_all(dirimage_matrix,0.0);

  // go over each beam model
  ii = 0;
  while (oblist[ii] != NULL)
    {

      // get the direct object for the actual model spectrum
      actdir = get_dirobject_from_list(dirlist, oblist[ii]->ID);

      // get the beam for the actual model spectrum
      actbeam = oblist[ii]->beams[0];

      // report onto the screen
      fprintf(stdout, "aXe_DIRIMAGE: modelling object %i ...", oblist[ii]->ID);

      // get the integrated direct image intensity in cps
      cps = get_cps_for_dirobject(tpass, actdir);

      // correct the direct image positions for
      // the offset values introduced by building
      // the direct image objects around the reference
      // position, which is shifted from the true
      // direct image position by XOFF and YOFF as
      // given in the aXe configuration file
      actdir->ix_min -= (int)floor(actdir->xy_off[actbeam.ID].x + 0.5);
      actdir->ix_max -= (int)floor(actdir->xy_off[actbeam.ID].x + 0.5);
      actdir->iy_min -= (int)floor(actdir->xy_off[actbeam.ID].y + 0.5);
      actdir->iy_max -= (int)floor(actdir->xy_off[actbeam.ID].y + 0.5);
      actbeam.refpoint.x -= actdir->xy_off[actbeam.ID].x;
      actbeam.refpoint.y -= actdir->xy_off[actbeam.ID].y;

      // go over each pixel in the direct object area
      for (nx=actdir->ix_min; nx<=actdir->ix_max; nx++)
	{
	  // make sure to be inside the image
	  if (nx < 0 || nx >=  (int)dirimage_matrix->size1)
	    // skip column if not
	    continue;
	  for (ny=actdir->iy_min; ny<=actdir->iy_max; ny++)
	    {
	      // make sure to be inside the image
	      if (ny < 0 || ny >=  (int)dirimage_matrix->size2)
		// skip element if not
		continue;

	      // fill the dpixel structure
	      dpixel.x = (double)nx;
	      dpixel.y = (double)ny;

	      // check whether there is
	      // an image template
	      if (actdir->dirim)
		// get the pixel intensity from the image template
		sval = get_diremission_value(actdir->dirim, dpixel.x - actbeam.refpoint.x, dpixel.y - actbeam.refpoint.y);
	      else
		// get the Gaussian pixel intensity;
		//
		// do a subsampling over the pixel
		// to get a more appropriate value for the
		// emission val
		sval = get_sub_emodel_value(dpixel, actbeam, actdir->drzscale);

	      // compute and set the new pixel values
	      value = gsl_matrix_get(dirimage_matrix, nx, ny) + sval*cps;
	      gsl_matrix_set(dirimage_matrix, nx, ny, value);
	    }
	}

      // release the space for the various structures
      fprintf(stdout, " Done\n");
      ii++;
    }

  // return the image matrix
  return dirimage_matrix;
}

double
get_cps_for_dirobject(interpolator *tpass, dirobject *actdir)
{
  interpolator *combine;

  double cps=0.0;

  // create aa new multiplicator
  // interpolator
  combine = combine_tpass_SED(tpass, actdir);

  // integrate over the interpolator
  cps = integrate_interpolator(combine);

  // release memory
  free_interp(combine);

  // return the cps value
  return cps;
}

double
integrate_interpolator(interpolator *combine)
{
  double integral=0.0;

  int index;

  for (index=1; index < combine->nvals-1; index++)
    // add the increment
    integral +=  combine->yvals[index] * (combine->xvals[index+1] - combine->xvals[index-1]);

  // dont forget the start and end piece
  integral +=  combine->yvals[0]                * (combine->xvals[1]                - combine->xvals[0]);
  integral +=  combine->yvals[combine->nvals-1] * (combine->xvals[combine->nvals-1] - combine->xvals[combine->nvals-2]);

  // then return the half
  return integral/2.0;
}

interpolator *
combine_tpass_SED(interpolator *tpass, dirobject *actdir)
{
  interpolator *combine;

  int index=0;
  int n_additional=0;
  int act_index;

  gsl_vector     *indep_data;

  // go over all SED data points
  for (index = 0; index < actdir->SED->npoints; index++)
    {
      // transform to Angstrom
      actdir->SED->wavelength[index] *= 10.0;

      // check whether the independent data point
      // falls in the area of the sensitivity
      if (actdir->SED->wavelength[index] < tpass->xmax
	  && actdir->SED->wavelength[index] >  tpass->xmin)
	// enhance the number
	// of additional data points
	n_additional += 1;
    }

  // allocate memory for the new independent value list
  indep_data   = gsl_vector_alloc(tpass->nvals + n_additional);

  // go over all sensitivity data points and fill in
  // the independent values
  for (index = 0; index < tpass->nvals; index++)
    gsl_vector_set(indep_data, index, tpass->xvals[index]);

  // set the counter to the next free index
  act_index = tpass->nvals;

  // go over all SED data points
  for (index = 0; index < actdir->SED->npoints; index++)
    // check whether the independent data point
    // falls in the area of the sensitivity
    if (actdir->SED->wavelength[index] < tpass->xmax
	&& actdir->SED->wavelength[index] >  tpass->xmin)
      {
	// fill in the additional independent data point
	// from the SED
	gsl_vector_set(indep_data, act_index, actdir->SED->wavelength[index]);

	// enhance the counter
        act_index += 1;
      }

  // sort the vector
  gsl_sort_vector(indep_data);

  // compose a new interpolator from the independent values
  combine = get_combined_tpass_SED(indep_data, tpass, actdir);

  // release memory in the weight vector
  gsl_vector_free(indep_data);

  // go over all SED data points
  for (index = 0; index < actdir->SED->npoints; index++)
    // transform to nm
    actdir->SED->wavelength[index] /= 10.0;

  // return the interpolator
  return combine;
}

interpolator *
get_combined_tpass_SED(gsl_vector *indep_data, interpolator *tpass, dirobject *actdir)
{
  interpolator *combine;

  gsl_vector_int *indep_weight;

  int n_new=0;
  int index;
  int act_index;

  double *xvals_new;
  double *yvals_new;

  // allocat memory for the weight vector
  indep_weight = gsl_vector_int_alloc(indep_data->size);

  // set all the weights
  gsl_vector_int_set_all(indep_weight, 1);

  // go over the independent data
  for (index = 1; index < (int)indep_data->size; index++)
    // check whether the current entry is equal the previous one
    if (gsl_vector_get(indep_data, index) ==  gsl_vector_get(indep_data, index-1))
      // set the weight of the current entry to zero
      gsl_vector_int_set(indep_weight,index, 0);

  // initialize the
  // final number
  n_new = 0;

  // go over the weight array
  for (index=0; index < (int)indep_weight->size; index++)
    // just count the weights
    n_new += gsl_vector_int_get(indep_weight, index);

  // allocate memory for the interpolator data
  xvals_new = (double *)malloc(n_new * sizeof(double));
  yvals_new = (double *)malloc(n_new * sizeof(double));

  // go over the weight array
  act_index=0;
  for (index=0; index < (int)indep_weight->size; index++)
    // check for weight
    if (gsl_vector_int_get(indep_weight, index))
      {
	// take the independent value from the vector
	xvals_new[act_index] = gsl_vector_get(indep_data, index);

	// compute the new dependent value bye multiplying
	// the brightness with the sensitivity
	yvals_new[act_index] = eval_interp(tpass, xvals_new[act_index])
	  * get_flux_from_SED(actdir->SED, xvals_new[act_index]);

	// enhance the counter
	act_index++;
      }

  // create a new interpolator
  combine = create_interp(n_new, TPASS_INTERP_TYPE, xvals_new, yvals_new);

  // release memory in the weight vector
  gsl_vector_int_free(indep_weight);

  // return the interpolator
  return combine;
}

/*
 * Function: get_filter_sensitivity
 * The function reads in a total passband file from fits format
 * and stroes the data as an interpolation function. The dependent
 * data values are transformed from throughput to sensitivity,
 * and the interpolation function is returned.
 *
 * Parameters:
 * @param tpass_file  - the full name of the total passband
 * @param tel_are     - the collecting area of the telescope
 *
 * Returns:
 * @return tpass    - the filter sensitivity
 */
interpolator *
get_filter_sensitivity(const char tpass_file[], const double tel_area)
{
  interpolator    *tpass;

  double h_erg  = 6.6260693E-27;
  double c_cm   = 2.99792458E+10;

  double factor = 0.0;

  int index=0;

  fprintf(stdout, "Load Total bandpass table :%s\n", tpass_file);
  tpass =  create_interp_ftable(tpass_file, 2,"WAVELENGTH","THROUGHPUT", TPASS_INTERP_TYPE);

  for (index=0; index < tpass->nvals; index++)
    {
      // compute the conversion factor
      factor = tel_area / (h_erg * c_cm / (1.0E-08 * tpass->xvals[index]));

      // apply the conversion factor
      tpass->yvals[index] = tpass->yvals[index] * factor;
    }

  // return the filter sensitivity
  return tpass;
}


#ifndef BIO_RUN_MATCH_H_
#define BIO_RUN_MATCH_H_

#include "bio/defs.h"
#include "bio/common.h"
#include "bio/match_hit.h"
#include "bio/sequence.h"
#include "bio/biobase_filter.h"
#include "bio/pssm_match.h"
#include "bio/phylogenetics.h"
#include "bio/biobase_db.h"
#include "bio/biobase_data_traits.h"

#include <boost/filesystem/path.hpp>

#include <vector>
#include <set>

#include <gsl/gsl_sf_log.h>
#include <gsl/gsl_sf_exp.h>

namespace boost { namespace program_options {
    //forward decl
    class options_description;
} }

BIO_NS_START

enum ScoreAlgorithm
{
    OTT_SCORE_ALGORITHM,
    BAYESIAN_SCORE_ALGORITHM
};

struct MatchParams
{
    MatchParams(
        float_t t,
        ScoreAlgorithm alg = OTT_SCORE_ALGORITHM,
        bool or_better = false)
        : threshold( t )
        , score_algorithm( alg )
        , or_better( or_better )
    {
    }

    float_t threshold;
    ScoreAlgorithm score_algorithm;
    bool or_better;
};

struct MatchResults
{
    MatchResults();

    MatchResults(
        TableLink link,
        Hit result);

    TableLink link;
    Hit result;
    size_t number;

    bool operator<(const MatchResults & rhs) const
    {
        if (result == rhs.result)
        {
            if (link == rhs.link)
            {
                return number < rhs.number;
            }
            else
            {
                return link < rhs.link;
            }
        }
        else
        {
            return result < rhs.result;
        }
    }

protected:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & link;
        ar & result;
        ar & number;
    }
};
typedef std::vector<MatchResults> match_result_vec_t;
typedef boost::shared_ptr< match_result_vec_t > match_result_vec_ptr_t;
void sort_by_position(match_result_vec_t & matches);
std::ostream & operator<<(std::ostream & os, const MatchResults & match);

/** Compare based initially on position, then link. */
struct MatchResultsPositionLessThan
{
    bool
    operator()(const MatchResults & h1, const MatchResults & h2) const
    {
        return
            h1.result.position < h2.result.position
            ||
            (
                h1.result.position == h2.result.position
                &&
                h1.link < h2.link
            );
    }
};

inline
std::ostream &
operator<<(std::ostream & os, const MatchResults & details) {
    os
        << details.link << ","
        << details.result;
    return os;
}

/**
Score all the pssm on the sequence between match_seq_begin and match_seq_end and put the results in result_insert_it.
Returns an estimate that the pssm binds the sequence.
*/
template <class Pssm, class ResultInsIt>
float_t
score_pssm(
    const Pssm & pssm,
    seq_t::const_iterator match_seq_begin,
    seq_t::const_iterator match_seq_end,
    const MatchParams & params,
    ResultInsIt & result_insert_it)
{
    Scorers scorers(match_seq_begin, match_seq_end);

    //const hit_vec_t & hit_results = scorers.bayesian_scores.get_result(pssm);
    const hit_vec_t & hit_results =
        OTT_SCORE_ALGORITHM == params.score_algorithm
            ? scorers.ott_normalised_scores.get_result(pssm)
            : ( params.or_better
                ? scorers.bayesian_scores.get_result(pssm)
                : scorers.bayesian_or_better_scores.get_result(pssm));

    float_t p_does_not_bind = 1.0;
    for (hit_vec_t::const_iterator i = hit_results.begin();
        hit_results.end() != i;
        ++i)
    {
        if (i->score > params.threshold)
        {
            p_does_not_bind *= ( float_t( 1.0 ) - i->score );

            MatchResults result(pssm.get_link(), *i);
            assert(result.result.position < (int) (match_seq_end - match_seq_begin)); //make sure the position is not too high
            *result_insert_it++ = result;
        }
    }
    return float_t( 1.0 ) - p_does_not_bind;
}

/** Score all the pssms between begin and end and put the results in result_insert_it. */
template <class PssmIt, class ResultInsIt>
size_t
score_pssms(
    PssmIt pssm_begin,
    PssmIt pssm_end,
    seq_t::const_iterator match_seq_begin,
    seq_t::const_iterator match_seq_end,
    const MatchParams & params,
    ResultInsIt & result_insert_it)
{
    size_t num_pssms_matched = 0;
    for ( ; pssm_begin != pssm_end; ++pssm_begin)
    {
        try
        {
            score_pssm(
                *(pssm_begin->second.get()),
                match_seq_begin,
                match_seq_end,
                params,
                result_insert_it);

            ++num_pssms_matched;
        }
        catch (const std::exception & ex)
        {
            std::cerr << "Could not score " << pssm_begin->second->get_name() << ": " << ex.what() << std::endl;
        }
        catch (const std::string & ex)
        {
            std::cerr << "Could not score " << pssm_begin->second->get_name() << ": " << ex << std::endl;
        }
        catch (const char * ex)
        {
            std::cerr << "Could not score " << pssm_begin->second->get_name() << ": " << ex << std::endl;
        }
        catch (...)
        {
            std::cerr << "Could not score " << pssm_begin->second->get_name() << std::endl;
        }
    }
    return num_pssms_matched;
}

template <class SeqIt, class ResultInsIt, class ConsFilter, class MatrixFilter>
void
pssm_match(
    SeqIt seq_begin,
    SeqIt seq_end,
    float_t threshold,
    ScoreAlgorithm algorithm,
    bool use_or_better,
    ConsFilter consensus_filter,
    MatrixFilter matrix_filter,
    ResultInsIt result_inserter)
{
    typedef boost::filter_iterator<MatrixFilter, Matrix::map_t::const_iterator> matrix_filter_it;
    typedef boost::filter_iterator<ConsFilter, Site::map_t::const_iterator> site_filter_it;

    const matrix_filter_it matrices_begin(matrix_filter, BiobaseDb::singleton().get_matrices().begin(), BiobaseDb::singleton().get_matrices().end());
    const matrix_filter_it matrices_end(matrix_filter, BiobaseDb::singleton().get_matrices().end(), BiobaseDb::singleton().get_matrices().end());

    const site_filter_it sites_begin(consensus_filter, BiobaseDb::singleton().get_sites().begin(), BiobaseDb::singleton().get_sites().end());
    const site_filter_it sites_end(consensus_filter, BiobaseDb::singleton().get_sites().end(), BiobaseDb::singleton().get_sites().end());

    if (matrices_begin == matrices_end && sites_begin == sites_end)
    {
        throw std::logic_error( "No PSSMs match filter" );
    }

    MatchParams params(threshold, algorithm, use_or_better);
    {
        //std::cout << "Scoring matrices" << std::endl;
        //const size_t num_matched =
            score_pssms(
                matrices_begin,
                matrices_end,
                seq_begin,
                seq_end,
                params,
                result_inserter);
//                ostream_iterator<MatchResults>(cout, "\n"));
        //std::cout << "Scored " << num_matched << " matrices" << std::endl;
    }

    {
        //std::cout << "Scoring sites" << std::endl;
        //const size_t num_matched =
            score_pssms(
                sites_begin,
                sites_end,
                seq_begin,
                seq_end,
                params,
                result_inserter);
//                ostream_iterator<MatchResults>(cout, "\n"));
        //std::cout << "Scored " << num_matched << " sites" << std::endl;
    }
}

void
pssm_match(
    const seq_t & match_seq,
    float_t threshold,
    ScoreAlgorithm algorithm,
    const boost::filesystem::path & file,
    const std::string & title,
    bool show_labels);

template <typename HitIt>
float_t
estimate_binding_prob(
    HitIt hit_begin,
    HitIt hit_end)
{
    float_t product = 1.0;
    for (HitIt hit = hit_begin; hit_end != hit; ++hit)
    {
        product *= float_t( 1.0f - hit->result.score );
    }
    return float_t(1.0 - product);
}

template <typename Exponent>
double
power(double x, Exponent y)
{
    return
        0 == x
            ? 0
            : gsl_sf_exp(y * gsl_sf_log(x));
}

/** Adjusts hits for occurence in phylogenetically conserved sequence. */
template <typename ResultIt>
void
adjust_hits(
    ResultIt results_begin,
    ResultIt results_end,
    seq_t::const_iterator seq_begin,
    seq_t::const_iterator seq_end,
    float_t threshold,
    ScoreAlgorithm algorithm)
{
    const MatchParams params(threshold, algorithm);

    //maintain a set of already adjusted PSSMs
    std::set<TableLink> already_adjusted;

    //for each result
    for (ResultIt result = results_begin; results_end != result; ++result)
    {
        //check we haven't checked this PSSM before
        if (already_adjusted.end() != already_adjusted.find(result->link))
        {
            //we have so ignore it
            continue;
        }

        //we need to estimate likelihood that it binds in phylogenetic sequence so run PSSM over sequence.
        match_result_vec_t phylo_hits;
        std::insert_iterator<bio::match_result_vec_t> inserter(phylo_hits, phylo_hits.begin());
        switch (result->link.table_id)
        {
        case MATRIX_DATA:
            score_pssm(
                *BiobaseDb::singleton().get_entry<MATRIX_DATA>(result->link),
                seq_begin,
                seq_end,
                params,
                inserter);
            break;

        case SITE_DATA:
            score_pssm(
                *BiobaseDb::singleton().get_entry<SITE_DATA>(result->link),
                seq_begin,
                seq_end,
                params,
                inserter);
            break;

        default:
            throw std::logic_error( "Unknown pssm type" );
        }

        const float_t binding_prob = estimate_binding_prob(phylo_hits.begin(), phylo_hits.end());

        //adjust all the hits for this PSSM
        for (ResultIt to_adjust = result; results_end != to_adjust; ++to_adjust)
        {
            if (to_adjust->link == result->link)
            {
                //to_adjust->result.score *= power(binding_prob, 5.0 * phylo_seq.conservation * phylo_seq.conservation);
                to_adjust->result.score *= binding_prob;
            }
        }

        //add to the list of already adjusted PSSMs
        already_adjusted.insert(result->link);
    }
}

/** Adjusts hits for occurence in phylogenetically conserved sequence. */
template <
    typename ResultIt,
    typename SeqRange >
void
adjust_hits_for_phylo_sequences(
    ResultIt results_begin,
    ResultIt results_end,
    const SeqRange & sequences,
    float_t threshold,
    ScoreAlgorithm algorithm)
{
    unsigned num_seqs = 0;
    BOOST_FOREACH( const typename boost::range_value< SeqRange >::type & seq, sequences )
    {
        adjust_hits(
            results_begin,
            results_end,
            seq.begin(),
            seq.end(),
            threshold,
            algorithm );
        ++num_seqs;
    }
    std::cout << "Adjusted hits for " << num_seqs << " sequences" << std::endl;

    for( ; results_begin != results_end; ++results_begin )
    {
        //adjust hits - raise to 1/#seqs
        results_begin->result.score =
            float_t(
                ( 0.0 == results_begin->result.score ) ? 0.0 : exp( log( results_begin->result.score ) / ( num_seqs + 1 ) ) );
    }
}

/** Adjust hits by power. */
struct RaiseHitToPower
{
    double power;

    RaiseHitToPower(double power);

    void operator()(MatchResults & hit) const;
};

/** Is a hit above a threshold? */
struct HitAboveThreshold
{
    float_t threshold;

    HitAboveThreshold(float_t threshold);

    bool operator()(const MatchResults & results) const;
};

BIO_NS_END

#endif //BIO_RUN_MATCH_H_


#pragma once

#include "SimpleEvaluator.h"
#include "BooleanDAG.h"
#include "Interface.h"
#include "SuggestionGenerator.h"
#include <map>
#include <set>
#ifndef _NOGSL
#include <gsl/gsl_vector.h>
#else
#include "FakeGSL.h"
#endif

class SmartSuggestionGenerator1: public SuggestionGenerator {
    Interface* interf;
    BooleanDAG* dag;
    map<string, int>& ctrls;
    SimpleEvaluator* seval;
    const vector<vector<int>>& dependentInputs;
    const vector<vector<int>>& dependentCtrls;
    vector<int> inputsToAsserts; // TODO: this is pretty local hack
    int counter;
public:
    SmartSuggestionGenerator1(BooleanDAG* _dag, Interface* _interface, map<string, int>& _ctrls, const vector<vector<int>>& _dependentInputs, const vector<vector<int>>& _dependentCtrls): dag(_dag), interf(_interface), ctrls(_ctrls), dependentInputs(_dependentInputs), dependentCtrls(_dependentCtrls) {
        seval = new SimpleEvaluator(*dag, ctrls);
        seval->setInputs(interf);
        counter = 0;
        inputsToAsserts.resize(dag->size(), -1);
        for (int i = 0; i < dag->size(); i++) {
            bool_node* n = (*dag)[i];
            if (n->type == bool_node::ASSERT) {
                const auto& inputs = dependentInputs[i];
				for (auto it = inputs.begin(); it != inputs.end(); ++it) {
                    inputsToAsserts[*it] = i;
                }
            }
        }
    }

    pair<int, int> getSuggestion(const gsl_vector* state) {
        return make_pair(-1, 0);
    }

    pair<int, int> getUnsatSuggestion(const gsl_vector* state) {
        return make_pair(-1, 0);
    }

    set<bool_node*> getResponsibleNodes(bool_node* node) {
        Assert(node->type == bool_node::ASSERT, "dsfqhuopwqe");
        set<bool_node*> responsibleNodes;
        vector<bool_node*> nodes; // queue
        nodes.push_back(node);

        while(nodes.size() > 0) {
            bool_node* n = nodes.back();
            nodes.pop_back();
            Assert(n->type == bool_node::LT || n->type == bool_node::ASSERT || n->type == bool_node::NOT || n->type == bool_node::AND || n->type == bool_node::OR, "Unexpected node type");
            if (n->type == bool_node::LT) {
                responsibleNodes.insert(n);
            }
            if (n->type == bool_node::ASSERT || n->type == bool_node::NOT) {
                nodes.push_back(n->mother());
            }
            if (n->type == bool_node::AND || n->type == bool_node::OR) {
                double dist = seval->d(n);
                double mdist = seval->d(n->mother());
                double fdist = seval->d(n->father());
                if (dist * mdist > 0.0) {
                    nodes.push_back(n->mother());
                }
                if (dist * fdist > 0.0) {
                    nodes.push_back(n->father());
                }
            }
        }
        return responsibleNodes;
    }

    virtual IClause* getConflictClause(int level, LocalState * state) {
        return NULL;
    }

    virtual SClause* getUnsatClause(const gsl_vector* state) {
        return NULL;
    }

    virtual SClause* getUnsatClause(const gsl_vector* state, const gsl_vector* initState) {
        return NULL;
    }

    vector<tuple<int, int, int>> getUnsatSuggestions(const gsl_vector* state) {
        vector<tuple<int, int, int>> suggestions;
        cout << "state: " << Util::print(state) << endl;
        seval->run(state);

        double minNegDist = GradUtil::MAXVAL;
        int minNegDistId = -1;
        for (int i = 0; i < dag->size(); i++) {
            bool_node* n = (*dag)[i];
            if (n->type == bool_node::ASSERT) {
                double dist = seval->d(n);
                if (dist < 0.0) {
                    if (dist < minNegDist) {
                        minNegDist = dist;
                        minNegDistId = n->id;
                    }
                }
            }
        }

        cout << "MinNegNode: " << (*dag)[minNegDistId]->lprint() << " with error " << minNegDist << endl;

        /*cout << "python test.py " << counter++ << " \"" << Util::print(state) << "\" data.txt";
        string  assertMsg = ((ASSERT_node*)(*dag)[minNegDistId])->getMsg();
        size_t start = assertMsg.find("(");
        if (start != string::npos) {
            size_t end = assertMsg.find(")");
            cout << " \"" << assertMsg.substr(start, end - start + 1) << "\"";
        }

        const set<int>& inputConstraints = interface->getInputConstraints();
        cout << " \"";
        for (auto it = inputConstraints.begin(); it != inputConstraints.end(); it++) {
            string line = to_string(dependentCtrls[*it][0]);
            string assertMsg = ((ASSERT_node*)(*dag)[inputsToAsserts[*it]])->getMsg();
            string point = "";
            size_t start = assertMsg.find("(");
            if (start != string::npos) {
                size_t end = assertMsg.find(")");
                point = assertMsg.substr(start, end - start + 1);
            }
            cout << line << ":" << point << ":" << interface->getValue(*it) << ";" ;
        }
        cout << "\"" << endl;*/

        map<int, int> ctrlsToScore;
        for (int i = 0; i < dag->size(); i++) {
            bool_node* n = (*dag)[i];
            if (n->type == bool_node::ASSERT) {
                bool satisfied = seval->d(n) >= 0.0;
                const set<bool_node*>& responsibleNodes = getResponsibleNodes(n);
                for (auto it = responsibleNodes.begin(); it != responsibleNodes.end(); it++) {
                    const vector<int>& ctrls = dependentCtrls[(*it)->id];
                    int cost = satisfied ? 1 : -1;
                    for (int j = 0; j < ctrls.size(); j++) {
                        if (ctrlsToScore.find(ctrls[j]) == ctrlsToScore.end()) {
                            ctrlsToScore[ctrls[j]] = cost;
                        } else {
                            ctrlsToScore[ctrls[j]] += cost;
                        }
                    }
                }
            }
        }

        for (auto it = ctrlsToScore.begin(); it != ctrlsToScore.end(); it++) {
            cout << "(" <<  it->first << "," << it->second << "); ";
        }
        cout << endl;

        const set<bool_node*>& influentialInputs = getResponsibleNodes((*dag)[minNegDistId]); // this should be a subset of dependentInputs of minNegDistId

        vector<tuple<int, int, int>> s;
        for (auto it = influentialInputs.begin(); it != influentialInputs.end(); it++) {
            int inputId = (*it)->id;
            double inputDist = seval->d(*it);
            int val = inputDist >= 0.0 ? 0 : 1;
            const vector<int>& ctrls = dependentCtrls[inputId];
            int cost = 0;
            for (int j = 0; j < ctrls.size(); j++) {
                if (ctrlsToScore.find(ctrls[j]) != ctrlsToScore.end()) {
                    cost += ctrlsToScore[ctrls[j]];
                }
            }
            s.push_back(make_tuple(cost, inputId, val));
            cout << (*dag)[inputId]->lprint() << " " << seval->d((*dag)[inputId]) << "  [" << Util::print(dependentCtrls[inputId])  << "] " << cost << endl;
        }
        sort(s.begin(), s.end());
        reverse(s.begin(), s.end());
        for (int k = 0; k < s.size(); k++) {
            int nodeid = get<1>(s[k]);
            if (!interf->hasValue(nodeid)) {
                suggestions.push_back(make_tuple(0, nodeid, get<2>(s[k])));
            }
        }
        int lastIdx = suggestions.size() - 1;
        if (lastIdx > 0) {
            cout << "Suggesting " << ((*dag)[get<1>(suggestions[lastIdx])])->lprint() << " " << get<2>(suggestions[lastIdx]) << endl;
        }

        return suggestions;

    }
};


#ifndef PROBABILITY_MODEL_H
#define PROBABILITY_MODEL_H

#include <vector>
#include <gsl/gsl_sf_gamma.h>
#include "data_input.h"
using namespace std;

class probability_model {

public:
	probability_model( unsigned long int end, unsigned long int diff);
    virtual ~probability_model();
    virtual double calculate_log_likelihood( const unsigned long int & position1, const unsigned long int & position2 ) = 0;
    virtual double calculate_log_likelihood( const vector< double > & ) = 0;
    unsigned long int get_diff() {return m_diff;}
    virtual void print_data() = 0;
    //virtual double calculate_total_log_likelihood() = 0;
    void get_cumulative_sufficient_data(const unsigned long int & time, vector< double > & sufficient_data);
    virtual void get_number_of_observations(const vector< double > & sufficient_stats, double & number_of_observations) = 0; // calculate the number of observations given a set of sufficient statistics
    unsigned long int m_end;
    unsigned long int m_diff;
    data_input * m_data;
};

probability_model::probability_model( unsigned long int end, unsigned long int diff ):m_end( end ), m_diff( diff){

}

probability_model::~probability_model(){

}

void probability_model::get_cumulative_sufficient_data(const unsigned long int & time, vector< double > & sufficient_data){
    m_data->get_cumulative_data_row(time, sufficient_data);
}

#endif


#ifndef _SPATIAL_CONVOLUTION_
#define _SPATIAL_CONVOLUTION_

#include "Mem_Alloc.h"
#include "Time.h"
#include "Global_Setting.h"
//#include <cblas.h>

extern int spatial_convolution_float32(float* input_ptr, float* kernel_ptr, float* output_ptr);
extern int spatial_convolution_float32_mm(float* input_ptr, float* kernel_ptr, float* output_ptr);
extern int spatial_convolution_float32_mm_blas(float* input_ptr, float* kernel_ptr, float* output_ptr);

#endif	//	_SPATIAL_CONVOLUTION_


#ifndef QDM_BIAS_H
#define QDM_BIAS_H

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

#include "evaluation.h"

gsl_matrix *
qdm_bias_values(
    qdm_evaluation *h,
    qdm_evaluation *f
);

gsl_matrix *
qdm_bias_correct(
    const gsl_vector *years,
    const double month,
    const gsl_vector *days,

    const gsl_vector *y,

    qdm_evaluation *fp,
    qdm_evaluation *ho,
    qdm_evaluation *hc
);

#endif /* QDM_BIAS_H */


/* ---------------------------------------------------------------- *
 * hod.h						            *
 * Martin Kilbinger, Henry J. McCracken, Jean Coupon 2008-2013      *
 * ---------------------------------------------------------------- */

#ifndef __HOD_H
#define __HOD_H

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

#include <gsl/gsl_sf_erf.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_roots.h>

#include "io.h"
#include "errorlist.h"
#include "config.h"
#include "maths.h"

#include "cosmo.h"
#include "nofz.h"
#include "halomodel.h"

/* errors */
#define HOD_BASE        -50000
#define HOD_MSTAR_MAX  HOD_BASE  + 1
#define HOD_NGAL_FIT   HOD_BASE  + 2

#define GM_base      -400000
#define GM_nz_type   GM_base + 1
#define GM_pi_max    GM_base + 2
#define GM_SHMR      GM_base + 3
#define GM_SMF       GM_base + 4
#define GM_GGL       GM_base + 5
#define GM_MSTAR_MAX GM_base + 6
#define GM_ETA       GM_base + 7

/* Limits for xi(r), 1- and 2-halo, in Mpc.h */
#define RMIN1 0.001
#define RMAX1 5.0
#define RMIN2 0.1
#define RMAX2 400.0

#define RMIN  RMIN1
#define RMAX  RMAX2
#define NR    50

#define MAXCHAR 1024
#define NLINES 100

#define NFIELD 100
#define NCHAR 20

/* GG: galaxy-galaxy stuff. GM: galaxy-dar matter stuff (lensing) */
#define GG 0
#define GM 1

#define getDoubleValue(array,col)  atof(array+NCHAR*(col-1))
#define getIntValue(array,col)     atoi(array+NCHAR*(col-1))
#define getCharValue(array,col)    array+NCHAR*(col-1)
#define getLine(array,i)           array+NFIELD*NCHAR*i

#define Nhalodata_t 4
typedef enum {w_of_theta, wp_rp, deltaSigma, smf} halodata_t;
#define shalodata_t(i) ( \
  i==w_of_theta    ? "w_of_theta" : \
  i==wp_rp         ? "wp_rp"      : \
  i==deltaSigma    ? "deltaSigma" : \
  i==smf           ? "smf"        : \
  "")

#define Nhalomode_t 3
typedef enum {galcorr_var, galcorr_cov, galcorr_log} halomode_t;
#define shalomode_t(i) (   \
 i==galcorr_var ? "galcorr_var" : \
 i==galcorr_cov ? "galcorr_cov" : \
 i==galcorr_log ? "galcorr_log" : \
 "")

#define Nngal_fit_t 5
typedef enum {ngal_log_fit, ngal_lin_fit, ngal_no_fit, ngal_lin_fit_only} ngal_fit_t;
#define sngal_fit_t(i) ( \
  i==ngal_log_fit  ? "ngal_log_fit"  : \
  i==ngal_lin_fit  ? "ngal_lin_fit"  : \
  i==ngal_no_fit   ? "ngal_no_fit"   : \
  i==ngal_lin_fit_only ? "ngal_lin_fit_only" : \
  "")

#define Nintconst_t 2
typedef enum {constant, random_file} intconst_t;
#define sintconst_t(i) ( \
  i==constant  ?    "constant"  : \
  i==random_file  ? "random_file"  : \
  "")

typedef struct {
  char WTHETA[500];
  char COVNAME[500];
  char DNDZ[500];
  char OUTFILE[500];
  int nbins;
  double ngal, ngalerr, area_deg, intconst, delta;
  double alpha_min,alpha_max,dalpha;
  double M1_min,M1_max,dM1;
  double Mmin_min,Mmin_max,dMmin;
  double Ngal_min,Ngal_max,dNgal;
  ngal_fit_t ngal_fit_type;
} config_hm_info;

/* ---------------------------------------------------------------- *
 * Global variables and functions                                   *
 * ---------------------------------------------------------------- */

#define ODD 0
#define EVEN 1

#define ABS(a) ((a) < 0 ? -(a) : (a))
#define PARITY(a) (a)%2 ? ODD : EVEN
#define FFTLog_SWAP(a,b) {FFTLog_TMP = (a); (a) = (b); (b) = FFTLog_TMP;}

#define CHANGE(fct) int change_##fct(cosmo_hm*, cosmo_hm*)
CHANGE(w_of_theta);
CHANGE(Pthg);
CHANGE(HOD);
CHANGE(ngd);
CHANGE(vc);
#undef CHANGE

/* ---------------------------------------------------------------- *
 * New types                                                        *
 * ---------------------------------------------------------------- */

/* This structure contains w_theta input data */
typedef struct {
  int nbins, nbins_RR;
  double *th;
  double *w;
  double *werr;   /* Error bars */
  double *wcov;   /* Covariance matrix */
  double *th_RR;  /* theta for random pairs (can be larger than for the data [even recommended]) */
  double *RR;     /* Random pairs (for the integral constraint) */
  double ngal;    /* Galaxy number density*/
  double ngal_err;
} wt_t;

typedef struct Nz_hist
{
  int nbins;
  double *z;
  double *n;
  double zmin;
  double zmax;
  double mean;
  double dz;
  double Norm;
  double max;
} Nz_hist;

/* ---------------------------------------------------------------- *
 * log-likelihood                                                   *
 * ---------------------------------------------------------------- */

double chi2_hm(cosmo_hm *model, halodata_t halodata, halomode_t halomode, const wt_t* data, ngal_fit_t ngal_fit_type,
	       double ngal, double ngalerr, intconst_t intconst_type, error **err);

/* ---------------------------------------------------------------- *
 * w(theta) and wp(rp)                                              *
 * ---------------------------------------------------------------- */

double *woftheta(cosmo_hm *model, pofk_t pofk, double *theta, int Ntheta, int i_bin, int j_bin, error **err);
double *woftheta_FFTLog_total(cosmo_hm *model, double ln_theta_min, double ln_delta_theta, int Ntheta,
			      double **theta, int i_bin, int j_bin, error **err);
double *wp(cosmo_hm *model, pofk_t pofk, const double *rp, int Nrp, double pi_max, int type, error **err);
double int_for_wp(double logr, void *params, error **err);

/* ---------------------------------------------------------------- *
 * Lensing functions (only for leauthaud11 model)                   *
 * ---------------------------------------------------------------- */

double *DeltaSigma(cosmo_hm *model, pofk_t pofk, const double *r, int N, error **err);
double int_for_Sigma(double logr, void *params, error **err);

/* ---------------------------------------------------------------- *
 * Stellar mass function (only for leauthaud11 model)               *
 * ---------------------------------------------------------------- */

double *dNdlogM10stellar(cosmo_hm *model, double *log10Mstellar, int N, error **err);
double *dNdlogM10stellar_c(cosmo_hm *model, double *log10Mstellar, int N, error **err);
double *dNdlogM10stellar_s(cosmo_hm *model, double *log10Mstellar, int N, error **err);

/* ---------------------------------------------------------------- *
 * HOD P(k) and xi(r)                                               *
 * ---------------------------------------------------------------- */

double *xiofr(cosmo_hm *model, pofk_t pofk, const double *r, int N, int type, error **err);
double *xi_1hcs(cosmo_hm *model,double a, const double *r, int N, int type, error **err);
double int_for_xi_1hcs(double logM, void *params, error **err);
double *xi_1hss(cosmo_hm *model,double a, const double *r, int N, int type, error **err);
double P1hss(double k, void *params);
double int_for_P1hss(double logM, void *params, error **err);
double concentration_sat(cosmo_hm *model, double Mh, double a, error **err);
double *xi_2h(cosmo_hm *model,double a, const double *r, int N, int type, error **err);
double *xi_P_NL(cosmo_hm *model, double a, const double *r, int N, error **err);
double FFTLog_P_NL(double k, void *params);
double P2h(double k, void *params);
double int_for_P2h(double logM, void *params, error **err);
double int_for_P2h_dm(double logM, void *params, error **err);
double bias_r(double M, void *params);

/* ---------------------------------------------------------------- *
 * HOD model functions                                              *
 * ---------------------------------------------------------------- */

double log10_fSHMR(double log10Mh, cosmo_hm *model);
double log10_fSHMR_inv_minus_x(double log10Mstar, void *p);
double log10_fSHMR_inv(double log10Mstar, cosmo_hm *model);
double Ngal_c(cosmo_hm *model, double Mh, double Mstellar_min, double Mstellar_max);
double int_for_Ngal_c(double Mstellar, void *params, error **err);
double phi_c_Mstellar(cosmo_hm *model, double log10Mstellar, double Mh, error **err);
double sigma_log_M(cosmo_hm *model, double log10Mstellar, error **err);
double eta_cen(cosmo_hm *model, double Mh, error **err);
double Ngal_s(cosmo_hm *model, double M, double Mstellar_min, double Mstellar_max);
double Ngal(cosmo_hm *model, double M, double Mstellar_min, double Mstellar_max);

double av_Mh_given_Mstar(cosmo_hm *model, double Mstellar, double a, error **err);
double int_for_phi_c_Mh(double log10Mh, void *params, error **err);
double int_for_phi_c_Mh_norm(double log10Mh, void *params, error **err);

/* ---------------------------------------------------------------- *
 * Deduced quantities                                               *
 * ---------------------------------------------------------------- */
double Mstar_tot_c(cosmo_hm *model, double Mh, double Mstellar_min, double Mstellar_max, error **err);
double int_for_Mstar_tot_c(double logMstar, void *params, error **err);
double Mstar_tot_s(cosmo_hm *model, double Mh, double Mstellar_min, double Mstellar_max, error **err);
double int_for_Mstar_tot_s(double logMstar, void *params, error **err);
double av_gal_bias(cosmo_hm *model, double a, error **err);
double int_for_av_gal_bias(double logM, void *intpar, error **err);
double av_halo_mass(cosmo_hm *model, double a, error **err);
double int_for_av_halo_mass(double logM, void *intpar, error **err);
double mass_weighted_av_stellar_mass(cosmo_hm *model, double a, error **err);
double int_for_mass_weighted_av_stellar_mass(double logM, void *params, error **err);
double int_for_mass_denum_weighted_av_stellar_mass(double logM, void *params, error **err);
double av_stellar_mass(cosmo_hm *model, double a, error **err);
double int_for_av_stellar_mass(double logM, void *params, error **err);
double av_frsat(cosmo_hm *model, double a, error **err);
double int_for_av_frsat(double logM, void *intpar, error **err);
double av_halo_bias(cosmo_hm *model, double a, error **err);
double int_for_av_halo_bias(double logM, void *intpar, error **err);
double dn_dlnM_integrand(double logM, void *intpar, error **err);

/* ---------------------------------------------------------------- *
 * Physical quantities                                              *
 * ---------------------------------------------------------------- */

double vc(cosmo_hm *model,double zmin, double zmax, error **err);
double int_for_vc(double z, void *params, error **err);
double logM_lim(cosmo_hm *model, double a, double r, error **err);
double ngal_triax(cosmo_hm *model, double a, double r,  error **err);
double ngal_den(cosmo_hm *model, double a, double logMlim, double Mstellar_min, double Mstellar_max, error **err);
double int_for_ngal_den(double logM, void *params,  error **err);
double ngal_den_c(cosmo_hm *model, double a, double logMlim, double Mstellar_min, double Mstellar_max, error **err);
double int_for_ngal_den_c(double logM, void *params,  error **err);
double ngal_den_s(cosmo_hm *model, double a, double logMlim, double Mstellar_min, double Mstellar_max, error **err);
double int_for_ngal_den_s(double logM, void *params,  error **err);
double ngal_den_vol(cosmo_hm *model, error **err);
double ngal_weighted(cosmo_hm *model, error **err);

/* ---------------------------------------------------------------- *
 * FFTLog                                                           *
 * ---------------------------------------------------------------- */

double xi_from_Pkr(gsl_function *ak, double r_prime, FFTLog_config *fc, error **err);
void FFTLog(FFTLog_config *fc, const gsl_function *ar_in,double *k, double *ak_out, int dir, error **err);
FFTLog_config *FFTLog_init(int N, double min, double max, double q, double mu);
void FFTLog_free(FFTLog_config *fc);
FFTLog_complex FFTLog_U_mu(double mu, FFTLog_complex z);

/* ---------------------------------------------------------------- *
 * Utils                                                            *
 * ---------------------------------------------------------------- */

void updateFrom_hm(cosmo_hm* avant, cosmo_hm* apres, error **err);
int change_vc(cosmo_hm *avant, cosmo_hm *apres);
int change_ngd(cosmo_hm *avant, cosmo_hm *apres);
int change_HOD(cosmo_hm *avant, cosmo_hm *apres);
int change_Pthg(cosmo_hm* avant, cosmo_hm* apres);
int change_w_of_theta(cosmo_hm *avant, cosmo_hm *apres);
wt_t *init_wt_t(double nlines, int wcov, error **err);
void free_wt_t(wt_t **model);
wt_t *read_wtheta(const char *data_file_name, intconst_t intconst_type, double delta, double intconst, const char *ran_file_name, error **err);
int getStrings(char *line, char *strings, char *delimit, size_t *N, error **err);
void read_config_hm_file(config_hm_info *config, char cname[], error **err);
Nz_hist *get_nz(redshift_t *nz_mk, int n_bin, error **err);
void  free_nz( Nz_hist *nz);
double normalize(double *data, size_t Nbins, double binsize,error **err);

/* ---------------------------------------------------------------- *
 * Obsolete stuff                                                   *
 * ---------------------------------------------------------------- */

double *woftheta_total_OBSOLETE(cosmo_hm *model, double log_theta_min, double log_delta_theta, int Ntheta, error **err);
nz_t *read_dndz_OBSOLETE(char *infile,error **err);
double Ngal_mean(cosmo_hm *model, double a, error **err);

double xir_mwolk(cosmo_hm *model, double a, double r, error **err);
wt_t *read_wtheta_mwolk(const char *name, double delta, double intconst, error **err);
double chi2_mwolk(cosmo_hm *model, const wt_t* data, ngal_fit_t ngal_fit_type,
		  double ngal, double ngalerr, double area, error **err);
double wp_mwolk(cosmo_hm *model, double rp, error **err);
double compute_chisq_wp(cosmo_hm *model, const wt_t *wth, double ngd_obs, double ngd_err,
			ngal_fit_t ngal_fit_type, double *ngd, int dologw, error **err);

#endif


/**
 * @file batchg_zgemm.c
 *
 *  @brief BBLAS batchg_zgemm double _Complex routine.
 *
 *  BBLAS is a software package provided by Univ. of Manchester,
 *  Univ. of Tennessee.
 *
 * @version 1.0.0
 * @author  Samuel  D. Relton
 * @author  Pedro   V. Lara
 * @author  Mawussi Zounon
 * @date    2016-06-03
 *
 **/

#ifndef DOXYGEN_SHOULD_SKIP_THIS
/**
 * Code generation
 * @precisions normal z -> c d s
 **/
#endif

#include <cblas.h>
#include "bblas.h"

#define COMPLEX

/**
    Purpose
    -------
    <b>batchg_zgemm</b> is a batch version of zgemm for groups
    of batched with different sizes.
	It performs the matrix-matrix operations

	arrayC[i] = alpha[i]*op( arrayA[i] )*op( arrayB[i] ) + beta[i]*arrayC[i],

	where op( X ) is one of

	op( X ) = X      or
	op( X ) = X**T   or
	op( X ) = X**H,

	alpha[i] and beta[i] are scalars, and arrayA[i], arrayB[i] and C are matrices, with
	op( arrayA[i] ) an m by k matrix, op( arrayB[i] ) a k by n matrix and arrayC[i]
	an m by n matrix.

	Group Batch Operations
	----------------------

    - all parameters that are arrays must have length at least group_count.
	- all parameters that are arrays must have all values set.

	Parameters
	----------
	@param[in]
	transA   Array of <tt>enum BBLAS_TRANS</tt>.
	         On entry, transA[i] specifies the form of op( arrayA[i] ) to be used in
			 the matrix multiplication as follows:
      -     = BblasNoTrans:    op( arrayA[i] ) = arrayA[i].
      -     = BblasTrans:      op( arrayA[i] ) = arrayA[i]**T.
      -     = BblasConjTrans:  op( arrayA[i] ) = arrayA[i]**H.

	@param[in]
	transB   Array of <tt>enum BBLAS_TRANS</tt>.
	         On entry, transB[i] specifies the form of op( arrayB[i] ) to be used in
             the matrix multiplication as follows:
      -     = BblasNoTrans:    op( arrayB[i] ) = arrayB[i].
      -     = BblasTrans:      op( arrayB[i] ) = arrayB[i]**T.
      -     = BblasConjTrans:  op( arrayB[i] ) = arrayB[i]**H.

	@param[in]
	M       Array of <tt>int</tt>.
            Each element M[i] specifies the number of rows of the matrix
            op( arrayA[i] ) and of the matrix arrayC[i]. M[i] must be greater than zero.

	@param[in]
	N       Array of <tt>int</tt>.
            Each element N[i] specifies the number of columns of the matrix
            op( arrayB[i] ) and the number of columns of the matrix arrayC[i].
 		    N[i] must be greater than zero.

    @param[in]
    K      Array of <tt>int</tt>.
	       Each element K[i] specifies the number of columns of the matrix
           op( arrayA[i] ) and the number of rows of the matrix op( arrayB[i] ).
		   K[i] must be greater than zero.

    @param[in]
    alpha   Array of <tt>complex_16</tt>.

    @param[in]
    arrayA  Array of pointers.
            Each element arrayA[i] is a pointer to a COMPLEX_16 matrix of
 		    dimension lda[i] by Ka[i], where Ka[i] is K[i] when transA[i] = BblasNoTrans,
 		    and is M[i] otherwise.
            When using transA[i] = BblasNoTrans the leading M[i] by K[i]
            part of arrayA[i] must contain the matrix elements, otherwise
            the leading  K[i] by M[i] part of arrayA[i] must contain the
            matrix elements.

    @param[in]
    lda     Array of <tt>int</tt>.
            Each element lda[i] specifies the first dimension of arrayA[i] as declared
            in the calling (sub) program. When transA[i] = BblasNoTrans then
            lda[i] must be at least max( 1, M[i] ), otherwise lda[i] must be at
            least max( 1, K[i] ).

    @param[in]
    arrayB  Array of pointers.
  		    Each element arrayB[i] is a pointer to a COMPLEX_16 matrix of
  		    dimension ldb[i] by Kb[i], where Kb[i] is N[i] when transB[i] = BblasNoTrans,
  		    and is K[i] otherwise.
            When using transB[i] = BblasNoTrans the leading K[i] by N[i]
            part of arrayB[i] must contain the matrix elements, otherwise
            the leading N[i] by K[i] part of arrayB[i] must contain the
            matrix elements.

    @param[in]
    ldb     Array of <tt>int</tt>.
  		    Each element ldb[i] specifies the first dimension of arrayB[i] as declared
            in the calling (sub) program. When transB[i] = BblasNoTrans then
            ldb[i] must be at least max( 1, K[i] ), otherwise ldb[i] must be at
            least max( 1, N[i] ).

    @param[in]
    beta    Array of <tt>complex_16</tt>.
            When beta[i] is set to zero arrayC[i] need not be set on input.

    @param[in,out]
    arrayC  Array of pointers.
	        Each element arrayC[i] is a pointer to a COMPLEX_16 matrix of
      	    dimension ldc[i] by N[i].
  	        Before entry, the leading M[i] by N[i] part of the arrayC[i] must
            contain a matrix C, except when beta is zero, in which
            case C need not be set on entry.
            On exit, the matrix arrayC[i] is overwritten by the M[i] by N[i] matrix
            ( alpha[i]*op( arrayA[i] )*op( arrayB[i] ) + beta[i]*arrayC[i] ).

    @param[in]
    ldc     Array of <tt>int</tt>.
  	        Each element ldc[i] specifies the first dimension of arrayC[i] as declared
  	        in the calling (sub) program. The value ldc[i] must be at least
  	        max( 1, M[i] )

    @param[in]
    batch_count  <tt>int</tt>
                 The number of matrices to operate on.

    @param[in,out]
    info    Array of <tt>int</tt>.
            Each element info[i] is the error return code of the ith group,
            these need not be set on entry.
  	    The error codes can be found in bblas_macros.h.
 **/
void batchg_zgemm(
    const enum BBLAS_TRANS *transA, const enum BBLAS_TRANS *transB,
    const int *M,  const int *N, const int *K,
    const BBLAS_Complex64_t *alpha,
    const BBLAS_Complex64_t **arrayA, const int *lda,
    const BBLAS_Complex64_t **arrayB,
    const int *ldb, const BBLAS_Complex64_t *beta,
    BBLAS_Complex64_t **arrayC, const int *ldc,
	const int group_count, const int *group_size,
    int *info)
{
    /* Local variables */
    int group_iter;
    int offset = 0;
    char func_name[15] = "batchg_zgemm";

	/* Check input arguments */
    if (group_count < 0)
    {
	    xerbla_batch(func_name, BBLAS_ERR_BATCH_COUNT, -1);
        return;
    }

	/* Check group_size and call fixed batch computation */
	for (group_iter = 0; group_iter < group_count; group_iter++)
	{
		if (group_size[group_iter] < 0)
		{
			xerbla_batch(func_name, BBLAS_ERR_GROUP_SIZE, group_iter);
		    info[group_iter] = BBLAS_ERR_GROUP_SIZE;
			continue;
		}

        /* Call to bblas_zgemm_batchf */
        batchf_zgemm(
            transA[group_iter],
            transB[group_iter],
            M[group_iter],
            N[group_iter],
            K[group_iter],
            alpha[group_iter],
            arrayA+offset,
            lda[group_iter],
            arrayB+offset,
            ldb[group_iter],
            beta[group_iter],
            arrayC+offset,
            ldc[group_iter],
            group_size[group_iter],
            &info[group_iter]);

        offset += group_size[group_iter];
    }
}
#undef COMPLEX


#pragma once

#include <gsl\gsl>
#include <winrt\Windows.Foundation.h>
#include <d3d11.h>
#include "DrawableGameComponent.h"
#include "MatrixHelper.h"

namespace Library
{
	class Mesh;
}

namespace Rendering
{
	class TexturedModelDemo final : public Library::DrawableGameComponent
	{
	public:
		TexturedModelDemo(Library::Game& game, const std::shared_ptr<Library::Camera>& camera);

		bool AnimationEnabled() const;
		void SetAnimationEnabled(bool enabled);

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

	private:
		struct CBufferPerObject
		{
			DirectX::XMFLOAT4X4 WorldViewProjection{ Library::MatrixHelper::Identity };
		};

		void CreateVertexBuffer(const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer) const;

		inline static const float RotationRate{ DirectX::XM_PI };

		DirectX::XMFLOAT4X4 mWorldMatrix{ Library::MatrixHelper::Identity };
		CBufferPerObject mCBufferPerObject;
		winrt::com_ptr<ID3D11VertexShader> mVertexShader;
		winrt::com_ptr<ID3D11PixelShader> mPixelShader;
		winrt::com_ptr<ID3D11InputLayout> mInputLayout;
		winrt::com_ptr<ID3D11Buffer> mVertexBuffer;
		winrt::com_ptr<ID3D11Buffer> mIndexBuffer;
		winrt::com_ptr<ID3D11Buffer> mConstantBuffer;
		winrt::com_ptr<ID3D11ShaderResourceView> mColorTexture;
		std::uint32_t mIndexCount{ 0 };
		float mRotationAngle{ 0.0f };
		bool mAnimationEnabled{ true };
		bool mUpdateConstantBuffer{ true };
	};
}


/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* This file was automatically generated --- DO NOT EDIT */
/* Generated on Sun Nov  7 20:45:10 EST 1999 */

#include <fftw-int.h>
#include <fftw.h>

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -hc2hc-backward 6 */

/*
 * This function contains 72 FP additions, 38 FP multiplications,
 * (or, 54 additions, 20 multiplications, 18 fused multiply/add),
 * 25 stack variables, and 48 memory accesses
 */
static const fftw_real K500000000 = FFTW_KONST(+0.500000000000000000000000000000000000000000000);
static const fftw_real K866025403 = FFTW_KONST(+0.866025403784438646763723170752936183471402627);
static const fftw_real K2_000000000 = FFTW_KONST(+2.000000000000000000000000000000000000000000000);
static const fftw_real K1_732050807 = FFTW_KONST(+1.732050807568877293527446341505872366942805254);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.41 1999/05/26 15:44:14 fftw Exp $
 * $Id: fft.ml,v 1.43 1999/05/17 19:44:18 fftw Exp $
 * $Id: to_c.ml,v 1.25 1999/10/26 21:41:32 stevenj Exp $
 */

void fftw_hc2hc_backward_6(fftw_real *A, const fftw_complex *W, int iostride, int m, int dist)
{
     int i;
     fftw_real *X;
     fftw_real *Y;
     X = A;
     Y = A + (6 * iostride);
     {
	  fftw_real tmp71;
	  fftw_real tmp75;
	  fftw_real tmp80;
	  fftw_real tmp82;
	  fftw_real tmp74;
	  fftw_real tmp76;
	  fftw_real tmp69;
	  fftw_real tmp70;
	  fftw_real tmp77;
	  fftw_real tmp81;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp69 = X[0];
	  tmp70 = X[3 * iostride];
	  tmp71 = tmp69 - tmp70;
	  tmp75 = tmp69 + tmp70;
	  {
	       fftw_real tmp78;
	       fftw_real tmp79;
	       fftw_real tmp72;
	       fftw_real tmp73;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp78 = Y[-2 * iostride];
	       tmp79 = Y[-iostride];
	       tmp80 = K1_732050807 * (tmp78 + tmp79);
	       tmp82 = K1_732050807 * (tmp78 - tmp79);
	       tmp72 = X[2 * iostride];
	       tmp73 = X[iostride];
	       tmp74 = tmp72 - tmp73;
	       tmp76 = tmp72 + tmp73;
	  }
	  X[3 * iostride] = tmp71 + (K2_000000000 * tmp74);
	  tmp77 = tmp71 - tmp74;
	  X[iostride] = tmp77 - tmp80;
	  X[5 * iostride] = tmp77 + tmp80;
	  X[0] = tmp75 + (K2_000000000 * tmp76);
	  tmp81 = tmp75 - tmp76;
	  X[2 * iostride] = tmp81 + tmp82;
	  X[4 * iostride] = tmp81 - tmp82;
     }
     X = X + dist;
     Y = Y - dist;
     for (i = 2; i < m; i = i + 2, X = X + dist, Y = Y - dist, W = W + 5) {
	  fftw_real tmp15;
	  fftw_real tmp46;
	  fftw_real tmp25;
	  fftw_real tmp52;
	  fftw_real tmp22;
	  fftw_real tmp35;
	  fftw_real tmp49;
	  fftw_real tmp62;
	  fftw_real tmp32;
	  fftw_real tmp39;
	  fftw_real tmp55;
	  fftw_real tmp59;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp13;
	       fftw_real tmp14;
	       fftw_real tmp23;
	       fftw_real tmp24;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp13 = X[0];
	       tmp14 = Y[-3 * iostride];
	       tmp15 = tmp13 + tmp14;
	       tmp46 = tmp13 - tmp14;
	       tmp23 = Y[0];
	       tmp24 = X[3 * iostride];
	       tmp25 = tmp23 - tmp24;
	       tmp52 = tmp23 + tmp24;
	  }
	  {
	       fftw_real tmp18;
	       fftw_real tmp47;
	       fftw_real tmp21;
	       fftw_real tmp48;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp16;
		    fftw_real tmp17;
		    fftw_real tmp19;
		    fftw_real tmp20;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp16 = X[2 * iostride];
		    tmp17 = Y[-5 * iostride];
		    tmp18 = tmp16 + tmp17;
		    tmp47 = tmp16 - tmp17;
		    tmp19 = Y[-4 * iostride];
		    tmp20 = X[iostride];
		    tmp21 = tmp19 + tmp20;
		    tmp48 = tmp19 - tmp20;
	       }
	       tmp22 = tmp18 + tmp21;
	       tmp35 = K866025403 * (tmp18 - tmp21);
	       tmp49 = tmp47 + tmp48;
	       tmp62 = K866025403 * (tmp47 - tmp48);
	  }
	  {
	       fftw_real tmp28;
	       fftw_real tmp54;
	       fftw_real tmp31;
	       fftw_real tmp53;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp26;
		    fftw_real tmp27;
		    fftw_real tmp29;
		    fftw_real tmp30;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp26 = Y[-2 * iostride];
		    tmp27 = X[5 * iostride];
		    tmp28 = tmp26 - tmp27;
		    tmp54 = tmp26 + tmp27;
		    tmp29 = Y[-iostride];
		    tmp30 = X[4 * iostride];
		    tmp31 = tmp29 - tmp30;
		    tmp53 = tmp29 + tmp30;
	       }
	       tmp32 = tmp28 + tmp31;
	       tmp39 = K866025403 * (tmp31 - tmp28);
	       tmp55 = tmp53 - tmp54;
	       tmp59 = K866025403 * (tmp54 + tmp53);
	  }
	  X[0] = tmp15 + tmp22;
	  {
	       fftw_real tmp36;
	       fftw_real tmp42;
	       fftw_real tmp40;
	       fftw_real tmp44;
	       fftw_real tmp34;
	       fftw_real tmp38;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp34 = tmp25 - (K500000000 * tmp32);
	       tmp36 = tmp34 - tmp35;
	       tmp42 = tmp35 + tmp34;
	       tmp38 = tmp15 - (K500000000 * tmp22);
	       tmp40 = tmp38 - tmp39;
	       tmp44 = tmp38 + tmp39;
	       {
		    fftw_real tmp33;
		    fftw_real tmp37;
		    fftw_real tmp41;
		    fftw_real tmp43;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp33 = c_re(W[1]);
		    tmp37 = c_im(W[1]);
		    Y[-3 * iostride] = (tmp33 * tmp36) - (tmp37 * tmp40);
		    X[2 * iostride] = (tmp37 * tmp36) + (tmp33 * tmp40);
		    tmp41 = c_re(W[3]);
		    tmp43 = c_im(W[3]);
		    Y[-iostride] = (tmp41 * tmp42) - (tmp43 * tmp44);
		    X[4 * iostride] = (tmp43 * tmp42) + (tmp41 * tmp44);
	       }
	  }
	  Y[-5 * iostride] = tmp25 + tmp32;
	  {
	       fftw_real tmp50;
	       fftw_real tmp56;
	       fftw_real tmp45;
	       fftw_real tmp51;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp50 = tmp46 + tmp49;
	       tmp56 = tmp52 - tmp55;
	       tmp45 = c_re(W[2]);
	       tmp51 = c_im(W[2]);
	       X[3 * iostride] = (tmp45 * tmp50) + (tmp51 * tmp56);
	       Y[-2 * iostride] = (tmp45 * tmp56) - (tmp51 * tmp50);
	  }
	  {
	       fftw_real tmp60;
	       fftw_real tmp66;
	       fftw_real tmp64;
	       fftw_real tmp68;
	       fftw_real tmp58;
	       fftw_real tmp63;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp58 = tmp46 - (K500000000 * tmp49);
	       tmp60 = tmp58 - tmp59;
	       tmp66 = tmp58 + tmp59;
	       tmp63 = tmp52 + (K500000000 * tmp55);
	       tmp64 = tmp62 + tmp63;
	       tmp68 = tmp63 - tmp62;
	       {
		    fftw_real tmp57;
		    fftw_real tmp61;
		    fftw_real tmp65;
		    fftw_real tmp67;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp57 = c_re(W[0]);
		    tmp61 = c_im(W[0]);
		    X[iostride] = (tmp57 * tmp60) + (tmp61 * tmp64);
		    Y[-4 * iostride] = (tmp57 * tmp64) - (tmp61 * tmp60);
		    tmp65 = c_re(W[4]);
		    tmp67 = c_im(W[4]);
		    X[5 * iostride] = (tmp65 * tmp66) + (tmp67 * tmp68);
		    Y[0] = (tmp65 * tmp68) - (tmp67 * tmp66);
	       }
	  }
     }
     if (i == m) {
	  fftw_real tmp1;
	  fftw_real tmp6;
	  fftw_real tmp4;
	  fftw_real tmp5;
	  fftw_real tmp9;
	  fftw_real tmp11;
	  fftw_real tmp12;
	  fftw_real tmp10;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp1 = X[iostride];
	  tmp6 = Y[-iostride];
	  {
	       fftw_real tmp2;
	       fftw_real tmp3;
	       fftw_real tmp7;
	       fftw_real tmp8;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp2 = X[2 * iostride];
	       tmp3 = X[0];
	       tmp4 = tmp2 + tmp3;
	       tmp5 = K1_732050807 * (tmp2 - tmp3);
	       tmp7 = Y[-2 * iostride];
	       tmp8 = Y[0];
	       tmp9 = tmp7 + tmp8;
	       tmp11 = K1_732050807 * (tmp7 - tmp8);
	  }
	  X[0] = K2_000000000 * (tmp1 + tmp4);
	  tmp12 = (K2_000000000 * tmp1) - tmp4;
	  X[2 * iostride] = tmp11 - tmp12;
	  X[4 * iostride] = tmp12 + tmp11;
	  X[3 * iostride] = K2_000000000 * (tmp6 - tmp9);
	  tmp10 = (K2_000000000 * tmp6) + tmp9;
	  X[iostride] = -(tmp5 + tmp10);
	  X[5 * iostride] = tmp5 - tmp10;
     }
}

static const int twiddle_order[] =
{1, 2, 3, 4, 5};
fftw_codelet_desc fftw_hc2hc_backward_6_desc =
{
     "fftw_hc2hc_backward_6",
     (void (*)()) fftw_hc2hc_backward_6,
     6,
     FFTW_BACKWARD,
     FFTW_HC2HC,
     146,
     5,
     twiddle_order,
};


/* movstat/funcacc.c
 *
 * Moving window accumulator for arbitrary user-defined function
 *
 * Copyright (C) 2018 Patrick Alken
 *
 * 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 <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_movstat.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_statistics.h>

typedef double funcacc_type_t;
typedef funcacc_type_t ringbuf_type_t;

#include "ringbuf.c"

typedef struct
{
  funcacc_type_t *window; /* linear array for current window */
  ringbuf *rbuf;          /* ring buffer storing current window */
} funcacc_state_t;

static size_t
funcacc_size(const size_t n)
{
  size_t size = 0;

  size += sizeof(funcacc_state_t);
  size += n * sizeof(funcacc_type_t);
  size += ringbuf_size(n);

  return size;
}

static int
funcacc_init(const size_t n, void * vstate)
{
  funcacc_state_t * state = (funcacc_state_t *) vstate;

  state->window = (funcacc_type_t *) ((unsigned char *) vstate + sizeof(funcacc_state_t));
  state->rbuf = (ringbuf *) ((unsigned char *) state->window + n * sizeof(funcacc_type_t));

  ringbuf_init(n, state->rbuf);

  return GSL_SUCCESS;
}

static int
funcacc_insert(const funcacc_type_t x, void * vstate)
{
  funcacc_state_t * state = (funcacc_state_t *) vstate;

  /* add new element to ring buffer */
  ringbuf_insert(x, state->rbuf);

  return GSL_SUCCESS;
}

static int
funcacc_delete(void * vstate)
{
  funcacc_state_t * state = (funcacc_state_t *) vstate;

  if (!ringbuf_is_empty(state->rbuf))
    ringbuf_pop_back(state->rbuf);

  return GSL_SUCCESS;
}

static int
funcacc_get(void * params, funcacc_type_t * result, const void * vstate)
{
  const funcacc_state_t * state = (const funcacc_state_t *) vstate;
  gsl_movstat_function *f = (gsl_movstat_function *) params;
  size_t n = ringbuf_copy(state->window, state->rbuf);

  *result = GSL_MOVSTAT_FN_EVAL(f, n, state->window);

  return GSL_SUCCESS;
}

static const gsl_movstat_accum func_accum_type =
{
  funcacc_size,
  funcacc_init,
  funcacc_insert,
  funcacc_delete,
  funcacc_get
};

const gsl_movstat_accum *gsl_movstat_accum_userfunc = &func_accum_type;


/* interpolation/spline.c
 *
 * Copyright (C) 2001, 2007 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 <string.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_spline.h>

gsl_spline *
gsl_spline_alloc (const gsl_interp_type * T, size_t size)
{
  gsl_spline * spline = (gsl_spline *) malloc (sizeof(gsl_spline));

  if (spline == NULL)
    {
      GSL_ERROR_NULL ("failed to allocate space for spline struct",
                      GSL_ENOMEM);
    }

  spline->interp = gsl_interp_alloc (T, size);

  if (spline->interp == NULL)
    {
      free (spline);
      GSL_ERROR_NULL ("failed to allocate space for interp", GSL_ENOMEM);
    };

  spline->x = (double *) malloc (size * sizeof(double));

  if (spline->x == NULL)
    {
      gsl_interp_free(spline->interp);
      free(spline);
      GSL_ERROR_NULL ("failed to allocate space for x", GSL_ENOMEM);
    }

  spline->y = (double *) malloc (size * sizeof(double));

  if (spline->y == NULL)
    {
      free(spline->x);
      gsl_interp_free(spline->interp);
      free(spline);
      GSL_ERROR_NULL ("failed to allocate space for y", GSL_ENOMEM);
    }

  spline->size = size;

  return spline;
}

int
gsl_spline_init (gsl_spline * spline, const double x_array[], const double y_array[], size_t size)
{
  if (size != spline->size)
    {
      GSL_ERROR ("data must match size of spline object", GSL_EINVAL);
    }

  memcpy (spline->x, x_array, size * sizeof(double));
  memcpy (spline->y, y_array, size * sizeof(double));

  {
    int status = gsl_interp_init (spline->interp, x_array, y_array, size);
    return status;
  }
}

const char *
gsl_spline_name(const gsl_spline * spline)
{
  return gsl_interp_name(spline->interp);
}

unsigned int
gsl_spline_min_size(const gsl_spline * spline)
{
  return gsl_interp_min_size(spline->interp);
}

void
gsl_spline_free (gsl_spline * spline)
{
  RETURN_IF_NULL (spline);
  gsl_interp_free (spline->interp);
  free (spline->x);
  free (spline->y);
  free (spline);
}

int
gsl_spline_eval_e (const gsl_spline * spline,
                   double x,
                   gsl_interp_accel * a, double *y)
{
  return gsl_interp_eval_e (spline->interp,
                            spline->x, spline->y,
                            x, a, y);
}

double
gsl_spline_eval (const gsl_spline * spline,
                 double x,
                 gsl_interp_accel * a)
{
  return gsl_interp_eval (spline->interp,
                          spline->x, spline->y,
                          x, a);
}

int
gsl_spline_eval_deriv_e (const gsl_spline * spline,
                         double x,
                         gsl_interp_accel * a,
                         double *dydx)
{
  return gsl_interp_eval_deriv_e (spline->interp,
                                  spline->x, spline->y,
                                  x, a, dydx);
}

double
gsl_spline_eval_deriv (const gsl_spline * spline,
                       double x,
                       gsl_interp_accel * a)
{
  return gsl_interp_eval_deriv (spline->interp,
                                spline->x, spline->y,
                                x, a);
}

int
gsl_spline_eval_deriv2_e (const gsl_spline * spline,
                          double x,
                          gsl_interp_accel * a,
                          double * d2)
{
  return gsl_interp_eval_deriv2_e (spline->interp,
                                   spline->x, spline->y,
                                   x, a, d2);
}

double
gsl_spline_eval_deriv2 (const gsl_spline * spline,
                        double x,
                        gsl_interp_accel * a)
{
  return gsl_interp_eval_deriv2 (spline->interp,
                                 spline->x, spline->y,
                                 x, a);
}

int
gsl_spline_eval_integ_e (const gsl_spline * spline,
                         double a, double b,
                         gsl_interp_accel * acc,
                         double * result)
{
  return gsl_interp_eval_integ_e (spline->interp,
                                  spline->x, spline->y,
                                  a, b, acc, result);
}

double
gsl_spline_eval_integ (const gsl_spline * spline,
                       double a, double b,
                       gsl_interp_accel * acc)
{
  return gsl_interp_eval_integ (spline->interp,
                                spline->x, spline->y,
                                a, b, acc);
}


/**
 *
 * @file core_dgbelr.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Azzam Haidar
 * @date 2011-05-15
 * @generated d Tue Jan  7 11:44:50 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

#define A(_m, _n)  (double *)plasma_geteltaddr(A, ((_m)-1), ((_n)-1), eltsize)
#define V(_m)      &(V[(_m)-1])
#define TAU(_m)    &(TAU[(_m)-1])

/***************************************************************************//**
 *
 * @ingroup CORE_double
 *
 *  CORE_dgbelr is a kernel that will operate on a region (triangle) of data
 *  bounded by st and ed. This kernel eliminate a column by an element-wise
 *  annihiliation, and for each annihiliation, it apply a left update, followed
 *  by an right update on the diagonal 2x2 element, where it create a new nnz,
 *  eliminate it and update V and TAU, then it continue until finishing the the
 *  whole column. When this is done, it take advantage that data are on cache
 *  and will apply the right on the remaining part of this region that has not
 *  been updated by the right yet.
 *  Note that the column to be eliminated is located at st-1.
 *
 *******************************************************************************
 *
 * @param[in] uplo
 *         @arg PlasmaLower:
 *         @arg PlasmaUpper:
 *
 * @param[in] N
 *          The order of the matrix A.
 *
 * @param[in, out] A
 *          A pointer to the descriptor of the matrix A.
 *
 * @param[out] V
 *          double array, dimension (N).
 *          The scalar elementary reflectors are written in this
 *          array. So it is used as a workspace for V at each step
 *          of the bulge chasing algorithm.
 *
 * @param[out] TAU
 *          double array, dimension (N).
 *          The scalar factors of the elementary reflectors are written
 *          in thisarray. So it is used as a workspace for TAU at each step
 *          of the bulge chasing algorithm.
 *
 * @param[in] st
 *          A pointer to the start index where this kernel will operate.
 *
 * @param[in] ed
 *          A pointer to the end index where this kernel will operate.
 *
 * @param[in] eltsize
 *          PLASMA internal value which refer to the size of the precision.
 *
 *******************************************************************************
 *
 * @return
 *          \retval PLASMA_SUCCESS successful exit
 *          \retval <0 if -i, the i-th argument had an illegal value
 *
 ******************************************************************************/
/***************************************************************************//**
 *  TYPE 1-BDL Householder
 *  add -1 because of C
 ******************************************************************************/
int
CORE_dgbelr(PLASMA_enum uplo, int N,
            PLASMA_desc *A,
            double *V,
            double *TAU,
            int st,
            int ed,
            int eltsize)
{
    int    NB, J1, J2;
    int    len1, len2, t1ed, t2st;
    int    i;
    static double zzero = 0.0;
    PLASMA_desc vA=*A;

    /* Check input arguments */
    if (N < 0) {
        coreblas_error(2, "Illegal value of N");
        return -2;
    }
    if (ed <= st) {
        coreblas_error(6, "Illegal value of st and ed (internal)");
        return -6;
    }

    /* Quick return */
    if (N == 0)
        return PLASMA_SUCCESS;

    NB = A->mb;
    if( uplo == PlasmaLower ){
        /* ========================
         *       LOWER CASE
         * ========================*/
        for (i = ed; i >= st+1 ; i--){
            /* generate Householder to annihilate a(i+k-1,i) within the band*/
            *V(i)          = *A(i, (st-1));
            *A(i, (st-1))  = zzero;
            LAPACKE_dlarfg_work( 2, A((i-1),(st-1)), V(i), 1, TAU(i));

            /* apply reflector from the left (horizontal row) and from the right for only the diagonal 2x2.*/
            J1    = st;
            J2    = i-2;
            t1ed  = (J2/NB)*NB;
            t2st  = max(t1ed+1,J1);
            len1  = t1ed-J1+1;
            len2  = J2-t2st+1;
            if(len1>0)CORE_dlarfx2(PlasmaLeft, len1 , *V(i), (*TAU(i)), A(i-1, J1  ), ELTLDD(vA, (i-1)),  A(i,  J1 ), ELTLDD(vA, i) );
            if(len2>0)CORE_dlarfx2(PlasmaLeft, len2 , *V(i), (*TAU(i)), A(i-1, t2st), ELTLDD(vA, (i-1)),  A(i, t2st), ELTLDD(vA, i) );
            CORE_dlarfx2ce(PlasmaLower, V(i), TAU(i), A(i-1,i-1), A(i,i-1), A(i,i));
        }
        /* APPLY RIGHT ON THE REMAINING ELEMENT OF KERNEL 1 */
        for (i = ed; i >= st+1 ; i--){
            J1    = i+1;
            J2    = min(ed,N);
            t1ed  = (J2/NB)*NB;
            t2st  = max(t1ed+1,J1);
            len1  = t1ed-J1+1;
            len2  = J2-t2st+1;
            if(len1>0)CORE_dlarfx2(PlasmaRight, len1, (*V(i)), (*TAU(i)), A(J1,i-1),   ELTLDD(vA, J1)  , A(J1  , i), ELTLDD(vA, J1)   );
            if(len2>0)CORE_dlarfx2(PlasmaRight, len2, (*V(i)), (*TAU(i)), A(t2st,i-1), ELTLDD(vA, t2st), A(t2st, i), ELTLDD(vA, t2st) );
        }
    } else {
        /* ========================
         *       UPPER CASE
         * ========================*/
        for (i = ed; i >= st+1 ; i--){
            /* generate Householder to annihilate a(i+k-1,i) within the band*/
            *V(i)          = *A((st-1),  i);
            *A((st-1),  i) = zzero;
            LAPACKE_dlarfg_work( 2, A((st-1), (i-1)), V(i), 1, TAU(i));

            /* apply reflector from the left (horizontal row) and from the right for only the diagonal 2x2.*/
            J1    = st;
            J2    = i-2;
            t1ed  = (J2/NB)*NB;
            t2st  = max(t1ed+1,J1);
            len1  = t1ed-J1+1;
            len2  = J2-t2st+1;
            if(len1>0)CORE_dlarfx2(PlasmaRight, len1, (*V(i)), (*TAU(i)), A(J1,i-1),   ELTLDD(vA, J1)  , A(J1  , i), ELTLDD(vA, J1)   );
            if(len2>0)CORE_dlarfx2(PlasmaRight, len2, (*V(i)), (*TAU(i)), A(t2st,i-1), ELTLDD(vA, t2st), A(t2st, i), ELTLDD(vA, t2st) );
            CORE_dlarfx2ce(PlasmaUpper, V(i), TAU(i), A((i-1),(i-1)), A((i-1), i), A(i,i));
        }
        /* APPLY LEFT ON THE REMAINING ELEMENT OF KERNEL 1*/
        for (i = ed; i >= st+1 ; i--){
            J1    = i+1;
            J2    = min(ed,N);
            t1ed  = (J2/NB)*NB;
            t2st  = max(t1ed+1,J1);
            len1  = t1ed-J1+1;
            len2  = J2-t2st+1;
            if(len1>0)CORE_dlarfx2(PlasmaLeft, len1 , *V(i), (*TAU(i)), A(i-1, J1  ), ELTLDD(vA, (i-1)),  A(i,  J1 ), ELTLDD(vA, i) );
            if(len2>0)CORE_dlarfx2(PlasmaLeft, len2 , *V(i), (*TAU(i)), A(i-1, t2st), ELTLDD(vA, (i-1)),  A(i, t2st), ELTLDD(vA, i) );
        }
    }  /* end of else for the upper case*/

    return PLASMA_SUCCESS;

}


// directdebye.c
// Computes the Debye sum from raytracing data.
// See man page for usage information.
#include <assert.h>
#include <complex.h>
#include <errno.h>
#include <error.h>
#include <gsl/gsl_const_mksa.h>
#include <malloc.h>
#include <math.h>
#include <matheval.h>
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "hdfio.h"
#include "miscprint.h"
#include "parseargs.h"
#include "rays.h"

int main(int argc, char **argv) {
  /* MPI initialization */
  int rank, nprocs;
  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  /* parse command line arguments */
  args_t *a = malloc(sizeof(args_t));
  parse_args(argc, argv, a);

  /* read the rays */
  unsigned int nrays; /* number of rays */
  ray_t *ray = NULL;
  ray = readrays(a->filename_in, &nrays);
  if (ray == NULL) {
    error_at_line(-1, errno, __FILE__, __LINE__, "Problem reading ray file.\n");
  }

  /* pulse function */
  void *pfcn_intensity = NULL;
  pfcn_intensity = evaluator_create(a->pfcn_intensity_buffer);
  if (pfcn_intensity == NULL) {
    error_at_line(-1, EINVAL, __FILE__, __LINE__,
                  "Problem creating pulse evaluator function.");
  }

  /* apodization function */
  void *pfcn_g = NULL;
  pfcn_g = evaluator_create(a->pfcn_g_buffer);
  if (pfcn_g == NULL) {
    error_at_line(-1, EINVAL, __FILE__, __LINE__,
                  "Problem creating apodization evaluator function.");
  }

  /* root node prints some useful information */
  if (rank == 0) {
    print_args(a, nrays);
  }

  /* Before allocating what could potentially be a lot of memory, check to
   * see if it's even possible.  */
  unsigned int i;
  a->efield_ss_out = 1;
  for (i = 0; i < 4; ++i) {
    a->efield_ss_out *= a->dim_efield_out[i];
  }

  long page_size = sysconf(_SC_PAGE_SIZE);
  // long pages_avail = sysconf(_SC_AVPHYS_PAGES);
  long pages_tot = sysconf(_SC_PHYS_PAGES);
  if (pages_tot * page_size < 8 * a->efield_ss_out * sizeof(complex double)) {
    error_at_line(-1, ENOMEM, __FILE__, __LINE__,
                  "Size of output arrays exceed avaliable memory.\n");
  }

  /* Allocate memory for the fields.  The _rcv is memory for nodes. */
  complex double *efield_x = NULL;
  complex double *efield_x_rcv = NULL;
  complex double *efield_y = NULL;
  complex double *efield_y_rcv = NULL;
  complex double *efield_z = NULL;
  complex double *efield_z_rcv = NULL;

  double *efield_r = NULL;
  double *efield_r_rcv = NULL;

  /* The root node will be receiving all of the data, so it gets the whole
   * thing.  Calloc here to pre-zero out everything. */
  if (rank == 0) {
    efield_x =
        (complex double *)calloc(a->efield_ss_out, sizeof(complex double));
    efield_y =
        (complex double *)calloc(a->efield_ss_out, sizeof(complex double));
    efield_z =
        (complex double *)calloc(a->efield_ss_out, sizeof(complex double));
    efield_r = (double *)calloc(4 * (a->efield_ss_out), sizeof(double));
  }

  /* The root node and all the other nodes also get this little chunk to
   * work on.  The root node really has nothing better to do while waiting
   * for information so there's no real waste. */
  efield_x_rcv = (complex double *)calloc(a->efield_ss_out / nprocs,
                                          sizeof(complex double));
  efield_y_rcv = (complex double *)calloc(a->efield_ss_out / nprocs,
                                          sizeof(complex double));
  efield_z_rcv = (complex double *)calloc(a->efield_ss_out / nprocs,
                                          sizeof(complex double));
  efield_r_rcv =
      (double *)calloc(4 * (a->efield_ss_out / nprocs), sizeof(double));

  /* Catch some possible malloc errors*/
  if (errno) {
    fprintf(stderr, "Node %d\n", rank);
    error_at_line(-1, errno, __FILE__, __LINE__, "Error allocating memory.");
  }

  unsigned int n;
  double w;
  /* precompute some ray information */
  for (n = 0; n < nrays; ++n, ++ray) {
    w = 2 * M_PI * GSL_CONST_MKSA_SPEED_OF_LIGHT * 1000 / ray->lambda;
    /* ray intensity for gaussian pulse */
    ray->intensity *= evaluator_evaluate_x(pfcn_intensity, w);
    if (isnan(ray->intensity)) {
      error_at_line(-1, errno, __FILE__, __LINE__,
                    "Error with intensity function: got nan.");
    }

    /* analytic apodization factor for parabola */
    ray->g *= evaluator_evaluate_x(pfcn_g, acos(ray->p0[2] / a->fs_radius));
    if (isnan(ray->g)) {
      error_at_line(-1, errno, __FILE__, __LINE__,
                    "Error with apodization function: got nan.");
    }

    /* all of the intensity information is stored in ray->g in order to
     * save a few multiplications in the inner loop */
    ray->g =
        (1.0 / (a->fs_radius * ray->lambda * 0.001)) * ray->g * ray->intensity;

    ray->e0[0] /= ray->lambda;
    ray->e0[1] /= ray->lambda;
    ray->e0[2] /= ray->lambda;
  }
  ray -= nrays;

  /* precompute the position vectors in the efield we'll be dealing with */
  if (rank == 0) {
    double dr[4];

    /* get the spacings of the electric field */
    for (i = 0; i < 4; ++i) {
      if (a->dim_efield_out[i] == 1) {
        dr[i] = (a->dim_efield_ub[i] - a->dim_efield_lb[i]) /
                ((double)a->dim_efield_out[i]);
      } else {
        dr[i] = (a->dim_efield_ub[i] - a->dim_efield_lb[i]) /
                ((double)a->dim_efield_out[i] - 1);
      }
    }

    /* set them */
    unsigned int r_i[4] = {};
    for (r_i[0] = 0; r_i[0] < a->dim_efield_out[0]; r_i[0]++) {
      for (r_i[1] = 0; r_i[1] < a->dim_efield_out[1]; r_i[1]++) {
        for (r_i[2] = 0; r_i[2] < a->dim_efield_out[2]; r_i[2]++) {
          for (r_i[3] = 0; r_i[3] < a->dim_efield_out[3]; r_i[3]++) {
            *efield_r++ = a->dim_efield_lb[0] + dr[0] * (double)r_i[0];
            *efield_r++ = a->dim_efield_lb[1] + dr[1] * (double)r_i[1];
            *efield_r++ = a->dim_efield_lb[2] + dr[2] * (double)r_i[2];
            *efield_r++ = a->dim_efield_lb[3] + dr[3] * (double)r_i[3];
          }
        }
      }
    }
    efield_r -= 4 * a->efield_ss_out;
  }

  /* send this spacing information out to everyone */
  MPI_Scatter(efield_r, 4 * (a->efield_ss_out / nprocs), MPI_DOUBLE,
              efield_r_rcv, 4 * (a->efield_ss_out / nprocs), MPI_DOUBLE, 0,
              MPI_COMM_WORLD);

  /* Main loop over ray data.  This should be fast as possible. */
  /* pre-computing the three efield_r_rcv lines doesn't appear to have a *
   * significant effect on performance */
  complex double tmp;
  for (i = 0; i < a->efield_ss_out / nprocs; ++i) {
    for (n = 0; n < nrays; ++n, ++ray) {
      tmp = -1.0i * ray->g *
            cexp(2.0i * M_PI *
                 ((*(efield_r_rcv + 0) - ray->p0[0]) * ray->e0[0] +
                  (*(efield_r_rcv + 1) - ray->p0[1]) * ray->e0[1] +
                  (*(efield_r_rcv + 2) - ray->p0[2]) * ray->e0[2] - ray->OPL -
                  *(efield_r_rcv + 3) * GSL_CONST_MKSA_SPEED_OF_LIGHT * 1000.0 /
                      ray->lambda +
                  a->temporal_offset * GSL_CONST_MKSA_SPEED_OF_LIGHT * 1000.0 /
                      ray->lambda));
      /* three polarization components */
      efield_x_rcv[i] += ray->pol[0] * tmp;
      efield_y_rcv[i] += ray->pol[1] * tmp;
      efield_z_rcv[i] += ray->pol[2] * tmp;
    }
    ray -= nrays;
    efield_r_rcv += 4;
  }
  efield_r_rcv -= 4 * i;

  /* gather all data from nodes */
  MPI_Gather(efield_x_rcv, a->efield_ss_out / nprocs, MPI_DOUBLE_COMPLEX,
             efield_x, a->efield_ss_out / nprocs, MPI_DOUBLE_COMPLEX, 0,
             MPI_COMM_WORLD);
  MPI_Gather(efield_y_rcv, a->efield_ss_out / nprocs, MPI_DOUBLE_COMPLEX,
             efield_y, a->efield_ss_out / nprocs, MPI_DOUBLE_COMPLEX, 0,
             MPI_COMM_WORLD);
  MPI_Gather(efield_z_rcv, a->efield_ss_out / nprocs, MPI_DOUBLE_COMPLEX,
             efield_z, a->efield_ss_out / nprocs, MPI_DOUBLE_COMPLEX, 0,
             MPI_COMM_WORLD);

  /* write electric field information to HDF file */
  if (rank == 0) {
    write_efield(a, efield_x, efield_y, efield_z);
  }

  /* clean up bit */
  free(efield_x);
  free(efield_y);
  free(efield_z);
  free(a);

  free(efield_x_rcv);
  free(efield_y_rcv);
  free(efield_z_rcv);
  free(efield_r_rcv);
  if (rank == 0) free(efield_r);
  free(ray);
  evaluator_destroy(pfcn_intensity);
  evaluator_destroy(pfcn_g);
  MPI_Finalize();
  return 0;
}


/**
 *
 * @file qwrapper_dlag2s.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated ds Tue Jan  7 11:44:57 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_dlag2s(Quark *quark, Quark_Task_Flags *task_flags,
                       int m, int n, int nb,
                       const double *A, int lda,
                       float *B, int ldb,
                       PLASMA_sequence *sequence, PLASMA_request *request)
{
    DAG_CORE_LAG2C;
    QUARK_Insert_Task(quark, CORE_dlag2s_quark, task_flags,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(double)*nb*nb,    A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(float)*nb*nb,    B,                 OUTPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_sequence*),           &sequence,  VALUE,
        sizeof(PLASMA_request*),            &request,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dlag2s_quark = PCORE_dlag2s_quark
#define CORE_dlag2s_quark PCORE_dlag2s_quark
#endif
void CORE_dlag2s_quark(Quark *quark)
{
    int m;
    int n;
    double *A;
    int lda;
    float *B;
    int ldb;
    PLASMA_sequence *sequence;
    PLASMA_request *request;
    int info;

    quark_unpack_args_8(quark, m, n, A, lda, B, ldb, sequence, request);
    info = LAPACKE_dlag2s_work(LAPACK_COL_MAJOR, m, n, A, lda, B, ldb);
    if (sequence->status == PLASMA_SUCCESS && info != 0)
        plasma_sequence_flush(quark, sequence, request, info);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_slag2d(Quark *quark, Quark_Task_Flags *task_flags,
                       int m, int n, int nb,
                       const float *A, int lda,
                       double *B, int ldb)
{
    QUARK_Insert_Task(quark, CORE_slag2d_quark, task_flags,
        sizeof(int),                        &m,     VALUE,
        sizeof(int),                        &n,     VALUE,
        sizeof(float)*nb*nb,    A,             INPUT,
        sizeof(int),                        &lda,   VALUE,
        sizeof(double)*nb*nb,    B,             INOUT,
        sizeof(int),                        &ldb,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slag2d_quark = PCORE_slag2d_quark
#define CORE_slag2d_quark PCORE_slag2d_quark
#endif
void CORE_slag2d_quark(Quark *quark)
{
    int m;
    int n;
    float *A;
    int lda;
    double *B;
    int ldb;

    quark_unpack_args_6(quark, m, n, A, lda, B, ldb);
    LAPACKE_slag2d_work(LAPACK_COL_MAJOR, m, n, A, lda, B, ldb);
}