#ifndef RECTREE_H
#define RECTREE_H
#include "tree.h"
#include "recedge.h"
#include "weakarg.h"
#include "mpiutils.h"
#include "wargxml.h"

#include <gsl/gsl_randist.h>
#define MAXNODES 100000

using namespace std;

namespace weakarg
{

class Param;

/**
    @brief Recombinant tree using Tree class
*/

class RecTree:public Tree
{
    friend class RecTreeAux;
protected:
// Variables
    std::vector<std::vector<int> > tabSons;///<Contains the branching order for the current local tree
    std::vector<std::vector<double> > tabSonsDist;///<Contains the distances for the current local tree
    std::vector<int> tabNode;///<Used only in makeLocalTree (made global for speed)
    std::vector<int> tabRec;///<Used only in makeLocalTree (made global for speed)
    std::vector<int> tabFather;///<Used only in makeLocalTree (made global for speed)
    std::vector<double> age;///<Used only in makeLocalTree (made global for speed)
    std::vector<int> tabEdge;///<Used only in makeLocalTree (made global for speed)
    std::vector<bool> sameLTasPrev;///<Indicates for each site whether the local tree is the same as that of the previous site
    unsigned int L;///< Number of sites in the sequences
    std::vector<RecEdge *> edge;///< The recombinant edges
// Functions
    int randomActiveNode(std::vector<int> nodelist);///<Returns a random node id from a vector of indicators for alive (-1 for not alive at current time)
    int getEdgeCoal(std::vector<int> nl,int k,double* time);///<Returns the node the recombinant edge is from and sets the time

    int moveClonalFixFrom(std::vector<int> ornodes=std::vector<int>(0));///< Fixes all from times to be in to valid nodes and reinstates **the vector ornodes. returns the number of recedges moved to younger nodes - number moved to older nodes (or -1 if this is not calculated)

    void updateEdgeTimes(int e,double reldist);///moves an edge when its node changes by proportion reldist
    void scaleEdges(int which, double dist);///<Scales the recedges "to" on edge which by an amount dist
    void fixFromTimes(int affedge,double dist);///<Fixes from times when we've moved edge affedge an amount dist
    void fixToTimes(int which, double dist);///<Fixes "to" times when we've changed an node time implicitly

    void swapEdge(int a, int b);///<Swaps two edges in the edge list (ordering only)
    void swapEdgeTo(int a, int b);///<Swaps the edges going to *two specified --nodes--*

    void orderEdges(int which);///<Checks that the edge which is in the correct place in the list (from 0.. which)

    inline int orderNodes(int which, double dist)
    {
	return(orderNodes(which,dist,-1));
    }///<places a node in a new place in the list (updated to account for recedges);
    int orderNodes(int which, double dist, int oldwhich);///<places a node in a new place in the list (updated to account for recedges); a known position is a positive oldwhich...
    void swapNode(int a, int b);///<Swaps two nodes in the list (updated to maintain recedges going to them)
    int lastCommonAncestor(int s1,int s2);///< USES TABNODES AND TABFATHER!  Returns the last common ancestor index of the tabnodes ASSUMING the current local tree.
    void makeLocalTreeKeepingFathers(unsigned int site);///< Makes a version of the local tree that has the fathers but is unoptimised
    double pairwiseDist(int s1,int s2);///< Returns the pairwise distance between two sequences accounting for recombination.  SAME WARNINGS AS lastCommonAncestor!
public:

// Constructors and destructors:
    RecTree(unsigned int numsites,WargXml *infile,bool addedges=true,bool forceages=true,bool isfinal=true,std::streampos itstart=-1);///<Creates a tree from an output file

    RecTree(unsigned int numsites,std::string newick,bool isFilename=true,bool forceages=true);///<Creates a tree from a Newick file
    RecTree(int n,double rho,double delta,std::vector<int> blocks);///<Creates a Coalescent tree with recombinant edges by simulation
    void dropEdges(double rho,double delta,vector<int> blocks);
    RecTree(Data * data,double rho,double delta,vector<int> blocks);///<Creates UPGMA tree
    RecTree(RecTree *intree,string newick,bool isFilename=false,bool forceages=true);/// Copies a rectree
    RecTree(const RecTree& rt);///< Copy constructor
    void assign(const RecTree& rt);
    ~RecTree();
// Part of the simulation initialisation:
    void setBlock(unsigned int* gstart,unsigned int* gend,double delta,std::vector<int> *blocks);///<Sets the start and end positions of a recombinant block based on geometric distribution and independent blocks
    int getEdgeCoal(double* time);///<Returns the node the recombinant edge is from and sets the time
// Part of the likelihood/prior calculation:
    double priorEdge(int e,Param * param) const;///<Returns the LOG prior of a given edge number in rectree
    double priorEdge(RecEdge* edge0,Param * param) const;///FMA_CHANGES: Computes prior of edge not in rectree
    double priorEdge(double tFrom,double tTo) const;///<Returns the UNLOGGED prior of a given edge
    double prior(Param * param) const;///<Returns the value of the prior function

    void makeLocalTree(unsigned int site,double thetaPerSite);///<Evaluates the local tree at the given site
// Tests:
    void testEdges() const;///<Tests recedges arrive in between the ages of the nodes they go to
    void testTree();///<Checks ages increase in the tree for all sites.
    inline bool sameLocalTreeAsPrev(unsigned int site) const
    {
        return sameLTasPrev[site];
    }///<Returns true if the local tree of site is the same as that of site-1
    void calcSameLTasPrev(unsigned int site);///<Estimate if the LT is the same as the previous one
// Information functions:
    inline Node * getRoot(){return(root);}
    inline double getEdgeTimeAbsFrom(int e) const
    {
        return edge[e]->getTimeFrom()+nodes[edge[e]->getEdgeFrom()]->getAge();
    }
    ;///<Returns the absolute age of the origin of a recombinant edge
    inline double getEdgeTimeAbsTo  (int e) const
    {
        return edge[e]->getTimeTo  ()+nodes[edge[e]->getEdgeTo  ()]->getAge();
    }
    ;///<Returns the absolute age of the destination of a recombinant edge
    inline int getL() const
    {
        return L;
    }///<Returns the total size of the alignment

    inline RecEdge*getRecEdge(int w) const
    {
        return edge[w];
    }///<Returns the w-th recombinant edge
    inline int getSon (int i, unsigned int site,bool isLeft,double *dist) const
    {
        site=0;
        *dist=tabSonsDist[i][isLeft];
        return tabSons[i][isLeft];
    };
    ///<Returns the left daughter node for a given local site and node (NULL if no left daughter)
    inline int affecting(unsigned int site) const
    {
        int a=0;
        for (unsigned int i=0;i<edge.size();i++)
            if (edge[i]->affectsSite(site))
                a++;
        return a;
    }
    ;///<Returns the number of edges affecting a site
    inline int numRecEdge() const
    {
        return edge.size();
    }///<Returns the number of recombinant edges
    int numRecEdgeOnBranch(int b) const
    {
        int r=0;
        for (unsigned int i=0;i<edge.size();i++)
            if (edge[i]->getEdgeTo()==b)
                r++;
        return r;
    }///<Returns the number of recedges on a given branch
    std::vector<int> alive(double t) const;///<Returns the branches alive at a given time
    bool isThere(int start,int end,int e,double time1,double time2) const;///<Returns yes if there is an edge with departure or arrival on branch e and between time1 and time2, and which affects material that intersects [start;end]
    inline RecEdge* getEdge(int i) const
    {
        return(edge[i]);
    }///<Returns the edge with index i
    double getEdgeTreeTime(int i) const;///<Returns the time an edge spans in the tree
    vector<int> getAffEdges(vector<int> * samplespace);///<Calculates the recedges relevent to a set of clonal edges
    void updateList(int which, int newloc,vector<int> * list);///<Updates a list of edges when which was moved to newloc
    int sampleEdge(vector<int> * samplespace=NULL);///<Samples a random edge that (optionally) interacts with a specific set of clonal edges
// Modification functions:
    void addEdgesFromFile(WargXml *infile,int siteoffset=0);///< adds edges to the rectree from the specified warg XML file (which is set to an iteration). Adds a "siteoffset" to add edges from different runs on different genes
    int addRecEdge(double tfrom,double tto,unsigned int gstart,unsigned int gend,int edgefrom,int edgeto);///<Adds an recombination edge to the tree
    int addRecEdge_FMA(double tfrom,double tto,unsigned int gstart,unsigned int gend,int edgefrom,int edgeto);///FMA_CHANGES: <Adds an recombination edge to the tree
    int addRecEdge(unsigned int gstart,unsigned int gend,int edgefrom,int edgeto);///< Adds a recombination edge with random start and end times between given edges
    int addRecEdge(std::string res,int sitesoffset=0);///<Add recedge from our output format
    void remRecEdge(int which);///<Removes a recombinant edge
    void setStart(int edge,int start);///<Sets the starting point of the given recedge
    void setEnd(int edge,int end);///<Sets the ending point of the given recedge
    void changeAge(int which,double dist);///<Changes the "which" node age, maintaining edge times
    inline void changeEdgeNodeFrom(int e,int newnode)
    {
        edge[e]->setTimeFrom(getEdgeTimeAbsFrom(e)-getNode(newnode)->getAge(),newnode);
    }///<changes the node to without changing the absolute time of the edge
    inline void changeEdgeNodeTo(int e,int newnode)
    {
        edge[e]->setTimeTo(getEdgeTimeAbsTo(e)-getNode(newnode)->getAge(),newnode);
    }///<changes the node to without changing the absolute time of the edge
    void chooseNodePath(int which, double dist, vector<int> *listLR, vector<int> *affedges);///< Sets up a specified order for which nodes to swap when they are disordered
    int moveNodeTime(int which, int *whichto,double dist,int oldwhich, vector<int> *listLR,  vector<int> *affedges);///<Updates the node list when the node which is moved a distance dist number of recedges moved to younger nodes - number moved to older nodes. edits affedges to make it the a vector of affected edges

    void moveEdges(int e1,int e2,double t1=0.0,double t2=-1.0);///<Moves all edge events on e1 between absolute times t1..t2 onto edge e2, maintaining their absolute times (t2=-1 means no upper limit)
// Information functions
    std::vector<std::vector<double> > pairwiseDistanceMatrix(long site);///< returns a pairwise distance matrix for a given site

};

} // end namespace weakarg
#endif


// Copyright 2016-2018 Lauri Juvela and Manu Airaksinen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DEFINITIONS_H_
#define DEFINITIONS_H_

#include <gslwrap/vector_int.h>
#include <gslwrap/vector_double.h>
#include <gslwrap/matrix_double.h>

/* Enums */
enum DataType {ASCII, DOUBLE, FLOAT};
enum SignalPolarity {POLARITY_DEFAULT, POLARITY_INVERT, POLARITY_DETECT};
enum LpWeightingFunction {NONE, AME, STE};
enum WindowingFunctionType {HANN, HAMMING, BLACKMAN, COSINE, HANNING, RECT, NUTTALL};
enum ExcitationMethod {SINGLE_PULSE_EXCITATION, DNN_GENERATED_EXCITATION,
   PULSES_AS_FEATURES_EXCITATION, EXTERNAL_EXCITATION, IMPULSE_EXCITATION};

/* Structures */
struct Param
{
	Param();
	~Param();

  public:
	int fs;
	int frame_length;
	int frame_length_long;
	int frame_length_unvoiced;
	int frame_shift;
	int number_of_frames;
	int signal_length;
	int lpc_order_vt;
	int lpc_order_glot;
	int hnr_order;
	bool use_external_f0;
	std::string external_f0_filename;
	bool use_external_gci;
	std::string external_gci_filename;
	bool use_external_lsf_vt; // inverse filtering with external vocal tract filter
	std::string external_lsf_vt_filename;
	bool use_external_excitation;
	std::string external_excitation_filename;

	std::string dnn_path_basename;
	std::string data_directory;
	std::string file_basename;
	std::string file_path;
	bool save_to_datadir_root;
	DataType data_type;
	bool qmf_subband_analysis;
	SignalPolarity signal_polarity;
	double gif_pre_emphasis_coefficient;
	double unvoiced_pre_emphasis_coefficient;
	bool use_iterative_gif;
	LpWeightingFunction lp_weighting_function;
	int lpc_order_glot_iaif;
	double warping_lambda_vt;
	double ame_duration_quotient;
	double ame_position_quotient;
	WindowingFunctionType default_windowing_function;
	WindowingFunctionType psola_windowing_function;
	WindowingFunctionType paf_analysis_window;

	double max_pulse_len_diff;
	int paf_pulse_length;
	bool use_pulse_interpolation;
	bool use_highpass_filtering;
	bool use_waveforms_directly;
	bool use_paf_unvoiced_synthesis;
	bool use_velvet_unvoiced_paf;
	bool extract_f0;
	bool extract_gain;
	bool extract_lsf_vt;
	bool extract_lsf_glot;
	bool extract_hnr;
	bool extract_infofile;
	bool extract_glottal_excitation;
	bool extract_original_signal;
	bool extract_gci_signal;
	bool extract_pulses_as_features;
	bool use_paf_energy_normalization;
	int lpc_order_vt_qmf1;
	int lpc_order_vt_qmf2;
	double f0_max;
	double f0_min;
	double voicing_threshold;
	double zcr_threshold;
	double relative_f0_threshold;
	double speed_scale;
	double pitch_scale;
	bool use_postfiltering;
	bool use_spectral_matching;
	bool use_wsola;
	bool use_wsola_pitch_shift;
	bool noise_gated_synthesis;
	double noise_reduction_db;
	double noise_gate_limit_db;
	double postfilter_coefficient;
	double postfilter_coefficient_glot;
	bool use_trajectory_smoothing;
	int lsf_vt_smooth_len;
	int lsf_glot_smooth_len;
	int gain_smooth_len;
	int hnr_smooth_len;
	int filter_update_interval_vt;
	int filter_update_interval_specmatch;
	double noise_gain_unvoiced;
	double noise_gain_voiced;
	double noise_low_freq_limit_voiced;
	//double f0_check_range;
	ExcitationMethod excitation_method;
	bool use_pitch_synchronous_analysis;
	bool use_generic_envelope;

	/* directory paths for storing parameters */
	std::string dir_gain;
	std::string dir_lsf;
	std::string dir_lsfg;
	std::string dir_hnr;
	std::string dir_paf;
	std::string dir_f0;
	std::string dir_exc;
	std::string dir_syn;
	std::string dir_sp;

	/* extensions for parameter types */
	std::string extension_gain;
	std::string extension_lsf;
	std::string extension_lsfg;
	std::string extension_hnr;
	std::string extension_paf;
	std::string extension_f0;
	std::string extension_exc;
	std::string extension_src = ".src.wav";
	std::string extension_syn;
	std::string extension_wav;

	std::string wav_filename;
	std::string default_config_filename;
	std::string user_config_filename;
};

/* Define analysis data variable struct*/
struct AnalysisData {
	AnalysisData();
	~AnalysisData();
	int AllocateData(const Param &params);
	int SaveData(const Param &params);
public:
	gsl::vector signal;
	gsl::vector fundf;
	gsl::vector frame_energy;
	gsl::vector_int gci_inds;
	gsl::vector source_signal;
   gsl::vector source_signal_iaif;

	gsl::matrix poly_vocal_tract;
	gsl::matrix lsf_vocal_tract;
	gsl::matrix poly_glot;
	gsl::matrix lsf_glot;
	gsl::matrix excitation_pulses;
   gsl::matrix hnr_glot;

	/* QMF analysis specific */
	//gsl::matrix lsf_vt_qmf1;
	//gsl::matrix lsf_vt_qmf2;
	//gsl::vector gain_qmf;
};

/* Define analysis data variable struct*/
struct SynthesisData {
   SynthesisData();
   ~SynthesisData();
public:
   gsl::vector signal;
   gsl::vector fundf;
   gsl::vector frame_energy;
   gsl::vector excitation_signal;

   gsl::matrix poly_vocal_tract;
   gsl::matrix lsf_vocal_tract;
   gsl::matrix poly_glot;
   gsl::matrix lsf_glot;
   gsl::matrix excitation_pulses;
   gsl::matrix hnr_glot;

   gsl::matrix spectrum;

   /* QMF analysis specific */
   //gsl::matrix lsf_vt_qmf1;
   //gsl::matrix lsf_vt_qmf2;
   //gsl::vector gain_qmf;
};

#endif


// Copyright 2011-2021 GameParadiso, Inc. All Rights Reserved.

#pragma once

#include <conio.h>

#include <map>
#include <unordered_set>
#include <unordered_map>
#include <ranges>
#include <iostream>
#include <sstream>
#include <chrono>
#include <random>
#include <thread>

#define FMT_HEADER_ONLY
#include <fmt/format.h>

#define GLM_FORCE_MESSAGES
#define GLM_FORCE_INLINE
#define GLM_FORCE_EXPLICIT_CTOR
#define GLM_FORCE_ALIGNED_GENTYPES
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/ext/scalar_constants.hpp>
#include <glm/gtc/type_aligned.hpp>

#include <gsl/gsl>

#include "Mathmatics.h"
#include "Util.h"

constexpr uint32_t NumEntities = 50000;


/*
MPCOTool:
The Multi-Purposes Calibration and Optimization Tool. A software to perform
calibrations or optimizations of empirical parameters.

AUTHORS: Javier Burguete and Borja Latorre.

Copyright 2012-2019, AUTHORS.

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 AUTHORS ``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 AUTHORS 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 mpcotool.c
 * \brief Main function source file.
 * \authors Javier Burguete and Borja Latorre.
 * \copyright Copyright 2012-2019, all rights reserved.
 */
#define _GNU_SOURCE
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <math.h>
#include <locale.h>
#include <gsl/gsl_rng.h>
#include <libxml/parser.h>
#include <libintl.h>
#include <glib.h>
#include <json-glib/json-glib.h>
#ifdef G_OS_WIN32
#include <windows.h>
#endif
#if HAVE_MPI
#include <mpi.h>
#endif
#if HAVE_GTK
#include <gio/gio.h>
#include <gtk/gtk.h>
#endif
#include "genetic/genetic.h"
#include "utils.h"
#include "experiment.h"
#include "variable.h"
#include "input.h"
#include "optimize.h"
#if HAVE_GTK
#include "interface.h"
#endif
#include "mpcotool.h"

#define DEBUG_MPCOTOOL 0        ///< Macro to debug main functions.

GMutex mutex[1];                ///< GMutex struct.
int ntasks;                     ///< Tasks number.
unsigned int nthreads;          ///< Threads number.

/**
 * Main function.
 *
 * \return 0 on success, >0 on error.
 */
int
mpcotool (int argn,             ///< Arguments number.
          char **argc)          ///< Arguments pointer.
{
  const struct option options[] = {
    {"seed", required_argument, NULL, 's'},
    {"nthreads", required_argument, NULL, 't'},
    {NULL, 0, NULL, 0}
  };
#if HAVE_GTK
  GtkApplication *application;
  char *buffer;
#endif
  int o, option_index;

  // Starting pseudo-random numbers generator
#if DEBUG_MPCOTOOL
  fprintf (stderr, "mpcotool: starting pseudo-random numbers generator\n");
#endif
  optimize->rng = gsl_rng_alloc (gsl_rng_taus2);

  // Allowing spaces in the XML data file
#if DEBUG_MPCOTOOL
  fprintf (stderr, "mpcotool: allowing spaces in the XML data file\n");
#endif
  xmlKeepBlanksDefault (0);

  // Starting MPI
#if HAVE_MPI
#if DEBUG_MPCOTOOL
  fprintf (stderr, "mpcotool: starting MPI\n");
#endif
  MPI_Init (&argn, &argc);
  MPI_Comm_size (MPI_COMM_WORLD, &ntasks);
  MPI_Comm_rank (MPI_COMM_WORLD, &optimize->mpi_rank);
  printf ("rank=%d tasks=%d\n", optimize->mpi_rank, ntasks);
#else
  ntasks = 1;
#endif

  // Getting threads number and pseudo-random numbers generator seed
  nthreads_climbing = nthreads = cores_number ();
  optimize->seed = DEFAULT_RANDOM_SEED;

  // Parsing command line arguments
  while (1)
    {
      o = getopt_long (argn, argc, "s:t:", options, &option_index);
      if (o == -1)
        break;
      switch (o)
        {
        case 's':
          optimize->seed = atol (optarg);
          break;
        case 't':
          nthreads_climbing = nthreads = atoi (optarg);
          break;
        default:
          printf ("%s\n%s\n", _("ERROR!"), _("Unknown option"));
          return 1;
        }
    }
  argn -= optind;

  // Resetting result and variables file names
#if DEBUG_MPCOTOOL
  fprintf (stderr, "mpcotool: resetting result and variables file names\n");
#endif
  input->result = input->variables = NULL;

#if HAVE_GTK

  // Setting local language and international floating point numbers notation
  setlocale (LC_ALL, "");
  setlocale (LC_NUMERIC, "C");
  window->application_directory = g_get_current_dir ();
  buffer = g_build_filename (window->application_directory, LOCALE_DIR, NULL);
  bindtextdomain (PROGRAM_INTERFACE, buffer);
  bind_textdomain_codeset (PROGRAM_INTERFACE, "UTF-8");
  textdomain (PROGRAM_INTERFACE);

  // Initing GTK+
  gtk_disable_setlocale ();
  application = gtk_application_new ("es.csic.eead.auladei.sprinkler",
                                     G_APPLICATION_FLAGS_NONE);
  g_signal_connect (application, "activate", G_CALLBACK (window_new), NULL);

  // Opening the main window
  g_application_run (G_APPLICATION (application), 0, NULL);

  // Freeing memory
  input_free ();
  g_free (buffer);
  gtk_widget_destroy (GTK_WIDGET (window->window));
  g_object_unref (application);
  g_free (window->application_directory);

#else

  // Checking syntax
  if (argn < 1 || argn > 3)
    {
      printf ("The syntax is:\n"
              "./mpcotoolbin [-nthreads x] [-seed s] data_file [result_file] "
              "[variables_file]\n");
      return 2;
    }
  if (argn > 1)
    input->result = (char *) xmlStrdup ((xmlChar *) argc[optind + 1]);
  if (argn == 2)
    input->variables = (char *) xmlStrdup ((xmlChar *) argc[optind + 2]);

  // Making optimization
#if DEBUG_MPCOTOOL
  fprintf (stderr, "mpcotool: making optimization\n");
#endif
  if (input_open (argc[optind]))
    optimize_open ();

  // Freeing memory
#if DEBUG_MPCOTOOL
  fprintf (stderr, "mpcotool: freeing memory and closing\n");
#endif
  optimize_free ();

#endif

  // Closing MPI
#if HAVE_MPI
  MPI_Finalize ();
#endif

  // Freeing memory
  gsl_rng_free (optimize->rng);

  // Closing
  return 0;
}


//
// Created by pedram pakseresht on 2/18/21.
//

// #include <petsc.h>

#include <printf.h>
#include <stdlib.h>

void setNumber(double *zxy);

typedef struct{

    double x;
    double z;
    int g[3];

}Pstruct;

void printStruct(Pstruct str) {

    printf("print struct %f %f %d %d %d \n", str.x, str.z, str.g[0], str.g[1], str.g[2]);
//    printf("print x %lf \n", str.z );
//    printf("print x %lf \n", str.g[1] );

};

void setValues(){

}

int main( int argc, char *argv[] )
{

    Pstruct a;
    a.x = 7.0;
    a.z = 8.0;
    a.g[0] = 1;
    a.g[1] = 2;
    a.g[2] = 3;

    Pstruct b;
    b.x = 5.0;
    b.z = 4.0;
    b.g[0] = 3;
    b.g[1] = 1;
    b.g[2] = 2;

    printStruct(a);
    printStruct(b);

//    printf("x of b is %lf \n",b.x);
//printf("size of struc %lu \n", sizeof(Pstruct));
//printf("size of int %lu", sizeof(int [4]));

}


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef texbatch_dfe6f3b4_430b_4424_98b6_d6b23235dc22_h
#define texbatch_dfe6f3b4_430b_4424_98b6_d6b23235dc22_h

#include <gslib/std.h>
#include <ariel/type.h>
#include <ariel/image.h>
#include <ariel/rectpack.h>
#include <ariel/rendersys.h>

__ariel_begin__

typedef render_texture2d texture2d;

#define _GS_BATCH_TEXTURE

#if defined(_GS_BATCH_TEXTURE)
#define location_key    texture2d*
#elif defined(_GS_BATCH_IMAGE)
#define location_key    const image*
#endif

class tex_batcher
{
public:
    typedef unordered_map<location_key, rectf> location_map;

public:
    tex_batcher();
    bool is_empty() const { return _rect_packer.is_empty(); }
    float get_width() const { return _rect_packer.get_width() + _gap; }
    float get_height() const { return _rect_packer.get_height() + _gap; }
    void add_image(const image* p);
    void add_texture(texture2d* p);
    void arrange();
    const location_map& get_location_map() const { return _location_map; }
    texture2d* create_texture(rendersys* rsys) const;
    void create_packed_image(image& img) const;
    void tracing() const;

protected:
    rect_packer         _rect_packer;
    location_map        _location_map;
    float               _gap;

private:
    void prepare_input_list(rp_input_list& inputs);
};

__ariel_end__

#endif


#ifndef __SVM_MAIN_H__
#define __SVM_MAIN_H__

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdlib.h>
#include <gsl_vector.h>
#include <gsl_multimin.h>
#include "common.h"
#include "smo.h"
#include "kernels.h"

// returns vector normal to hyperplane and margin width
PyObject* __get_w_b(PyObject* elements, int k_type, double C);
input_data_t* process_input_data(PyObject* n_array, int k_type, double C);

#endif  /* __SVM_MAIN_H__ */


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

#include <gsl/gsl_errno.h>

#include "ccl.h"

// Debug mode policy: whether to print error messages as they are raised.
// Defualt is ON.
static CCLDebugModePolicy _ccl_debug_mode_policy = CCL_DEBUG_MODE_ON;

// Set debug mode policy
void ccl_set_debug_policy(CCLDebugModePolicy debug_policy) {
    _ccl_debug_mode_policy = debug_policy;
}

// Convenience function to handle warnings
void ccl_raise_warning(int err, const char* msg, ...) {
  char message[256];

  va_list va;
  va_start(va, msg);
  vsnprintf(message, 250, msg, va);
  va_end(va);

  // For now just print warning to stderr if debug is enabled.
  // TODO: Implement some kind of error stack that can be passed on to, e.g.,
  // the python binding.
  if (_ccl_debug_mode_policy == CCL_DEBUG_MODE_ON) {
    fprintf(stderr, "WARNING %d: %s\n", err, message);
  }
}

// Convenience function to handle warnings
void ccl_raise_gsl_warning(int gslstatus, const char* msg, ...) {
  char message[256];

  va_list va;
  va_start(va, msg);
  vsnprintf(message, 250, msg, va);
  va_end(va);

  ccl_raise_warning(gslstatus, "%s: GSL ERROR: %s", message, gsl_strerror(gslstatus));
  return;
}


/*************************************************************************

description:
This file contains the class that can do the actual radiative transport,
including routines for grid calculation, physics and the actual transport

Copyright Jan-Pieter Paardekooper and Chael Kruip October 2011

This file is part of SimpleX.

SimpleX 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.

SimpleX 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 SimpleX.  If not, see <http://www.gnu.org/licenses/>.

**************************************************************************/

#ifndef SIMPLEX_H
#define SIMPLEX_H

#ifndef NOMPI
#include "mpi.h"
#endif

#include "rates.h"
#include "Common.h"
#include "Structs.h"
#include <gsl/gsl_rng.h>  //random number generator
#include "configfile.h"   //keyvalue input file

#ifdef HEAL_PIX
  #include "healpix_base.h" //healpix header
#endif

#include "tree_structures.h" //octree
#include "hilbert.h"         //hilbert curve

#include "h5w_serial.h"      //hdf5 header

#include <algorithm>

#if defined(__cplusplus)
extern "C"
{
#endif
#include <stdio.h>
#include <stdlib.h>
#include <libqhull.h>
#include <mem.h>
#include <qset.h>
#include <geom.h>
#include <merge.h>
#include <poly.h>
#include <io.h>
#include <stat.h>
#if defined(__cplusplus)
}
#endif

using namespace std;

#define VERTEX_ITERATOR vector< Vertex >::iterator
#define SITE_ITERATOR vector< Site >::iterator
#define SIMPL_ITERATOR vector< Simpl >::iterator

//! Class in which all functions that work on the SimpleX grid are stored

//! This is the main class of the simulation!
class SimpleX{

  public:
    //! constructor
    SimpleX(const string& output_path = ".", const string& data_path = ".");
    //! destructor
    ~SimpleX();

  //================ Grid Initilisation Functions ==================================//

    //! Create triangulation

    //! Initialize the simulation by creating/reading the vertex list and compute the triangulation
    //! Result is a list of Sites and a list of Simplices
    void init_triangulation(char* inputName);

    //TEST
    void reinit_triangulation();
    //END TEST

    //! Read in parameter file
    void read_parameters( char* inputName );

    //! Create orientations and mappings from correct header file
    void set_direction_bins();

    //! Create a simple homogeneous point distribution

    //! Points are placed using the gsl randoom number generator
    void poisson_square();

    //! Read in hdf5 file with site information
    void read_vertex_list();

    //! Compute the largest index of all the vertices
    unsigned long long int computeMaxId();

    //! Create boundary around computational domain

    //! Width of the boundary is variable borderBox.
    //! Number of points in the boundary is variable borderSites.
    void create_boundary();

    //!Create octree containing all vertices

    //!Size of the tree depends on the number of subboxes used, which
    //!is determined by the hilbert order m
    void create_vertex_tree();

    //! Decompose the domain

    //! Count the number of sites contained in one subbox and add those until
    //! number of points is approximately equal per processor
    void decompose_domain();

    //! Assign the correct process to vertices
    void assign_process();

    //! Check if site is in unit domain
    bool inDomain(const float& x, const float& y, const float& z, const unsigned int& rank);

    //! Compute the triangulation

    //! Call to QHull to perform the triangulation of vertices in one subbox
    //! The result is a vector of simplices
    //! Also, a check is done whether the subbox and domain boundaries are
    //! sufficiently large. If not, subbox boundaries are extended. If the
    //! boundary around the unity domain is too small, the code exits,
    //! to avoid memory issues when this number would be increased
    void compute_triangulation();

    //! Create the sites array on which radiative transfer is performed

    //! From the list of simplices the vertices relevant for this proc are selected and
    //! put in the sites vector
    void create_sites();

    //! Give each site an id corresponding to place in local sites vector
    void assign_site_ids();

    //! Determine which sites use ballistic transport and which sites
    //! should use direction conserving transport at the start of the
    //! simulation.
    void initiate_ballistic_sites();

    //! Shuffle list of sites

    //! This is necessary since the sites list is created from the simplices returned by QHull,
    //! so they are ordered.
    void shuffle_sites();

  //================ Site Properties Functions ==================================//

    //! Compute properties of the sites from the triangulation

    //! Important properties like the neighbours, the volumes and
    //! the most straight paths are computed
    void compute_site_properties();

    //! Compute the neighbours of vertex

    //! Generate the neighbour vectors for each vertex from the simplex vector
    //! Care should be taken so that no vertex is put twice in the neighbour list.
    //! Also get rid of vertices in the sub box boundary that are not connected to a site
    //! in the computational domain
    void compute_neighbours();

    //! Compute the volume of the Voronoi cell

    //! Use the volumes of the simplices that the vertex is part of to compute
    //! the volume of the Voronoi cell around the vertex
    void compute_volumes();

    //! Calculate the most straight paths for every incoming direction

    //! Needed for fast radiative transport
    void calculate_straight();

    //! Identify the place of every ballistic site in neighbour array
    //! of neighbouring site
    void match_neighbours();

    //! Compute the solid angles into which the intensity is distributed
    void compute_solid_angles( const bool& rotate );

    //! Check for cyclic connections in the grid and remove them if possible
    void check_cyclic_connections();

    //! Calculate mean distance to neighbours
    void calculate_line_lengths();

    //! Create list without border simplicesc
    void remove_border_simplices();

    //================ Grid Update Routines ============================//

    //! Rotate the solid angles of the unit sphere
    //! to avoid preferential directions
    void rotate_solid_angles();

    //! calculate normal distributed random numbers using the Box-Muller transformation
    void randGauss( float& y1, float& y2 );

    //! Store the intensities for use in later run, directions are kept.
    void store_intensities();

    //! Get the ballistic sites that have intensity to be stored
    const vector< unsigned long int > get_ballistic_sites_to_store();

    //! Store ballistic intensities in unit sphere tesselation
    void store_ballistic_intensities( const vector< unsigned long int >& sites_to_store );

    //! Return the intensities from the previous run
    void return_intensities();

    //! Return the intensities to ballistic sites
    void return_ballistic_intensities();

    //! Check which sites are ballistic and which not
    //! according to user specified switch

    //! Only used is case of combined transport
    void check_ballistic_sites();

    //! Create new sites array without the removed sites
    void store_site_properties();

    //! Create updated vertex list from site_properties vector
    void create_new_vertex_list();

    //================ Send Routines ===================================//

    //! Send vertices to processors
    void send_vertices();

    //! Send the domain decomposition to all procs
    void send_dom_dec();

    //! Fill the list of indices of sites that contain information to be send
    //! to other procs
    void fill_send_list();

    //! Give every vertex its correct process (needed for vertices in boundary between procs)
    void send_site_properties();

    //! Give every ballistic site its correct neighbour information

    //! This is only needed for ballistic sites in boundary between procs, and is necessary because
    //! the order of the neighbours in the neighbour vector is not the same among procs.
    void send_neighbour_properties();

    //! Send teh updated ballistic sites to all procs
    void send_site_ballistics( const vector< unsigned long int >& sites_to_send );

     //! Send the intensities among procs
    void send_intensities();

    //! Send site physics to all procs

    //! In case sites change from proc send physics
    void send_site_physics();

    //! In case sites change from proc send intensities
    void send_site_intensities();

    //! Send stored intensities among procs
    void send_stored_intensities();

    //! Send the updated vertex list to all procs
    void send_new_vertex_list();

    //================ Physics Functions ==================================//

    //! Compute all the physics needed to do radiative transport
    void compute_physics( const unsigned int& run );

    //! Initialize physical parameters
    void initialise_physics_params();

    //! Read metal line cooling data and initialize vectors
    void read_metals();

    //! set homogeneous number density.

    //! Use only in combination with Poisson_Square()
    void set_homogeneous_number_density( const float& nH );

    //! Give the masses and fluxes that were read in to the sites

    //! Use only in combination with read_vertex_list()
    void assign_read_properties();

    //! Give the updates sites their correct physical quantities from stored values
    void return_physics();

    //! Calculate the bounds of the frequency bins
    vector<double> calc_freq_bounds( const double& lowerBound, const double& upperBound );

    //! calculate effective cross section and spectrum of black body source
    void black_body_source( const double& tempSource );

    //! Compute the total number of atoms on this proc, used for output
    void output_number_of_atoms();

    //! Return mean optical depth of total grid
    double output_optical_depth();

    //! output relevant physical parameters to screen
    void parameter_output( const unsigned int& run );

    //! Add more neighbours to vertices that have a flux, by adding the neighbours
    //! of neighbours.
    void make_source_isotropic();

    //! calculate recombination
    double recombine();

    //! Rate of energy loss in cell
    double cooling_rate( Site& site );

    //! Rate of energy gain in cell
    double heating_rate( const vector<double>& N_ion, const double& t_end );

    //! compute mean molecular weight
    double compute_mu( Site& );

    //!Convert internal energy to temperature
    double u_to_T( const double& u, const double& mu );

    //!Convert temperature to internal energy
    double T_to_u( const double& T, const double& mu );

    //! update the temperature by including heating and cooling processes
    double update_temperature( Site& site, const vector<double>& N_ion, const double& t_end );

 //================= Radiative Transfer Functions ======================//

    //! Solve the rate equation to determine ionisations and recombinations
    vector<double> solve_rate_equation( Site& site );

    //! Send source photons
    void source_transport( Site& site );

    //! Transport photons diffusely
    void diffuse_transport( Site& site, vector<double>& N_out_total );

    //! Redistribute intensity: actual radiative transport
    void non_diffuse_transport( Site& site, vector<double>& N_out_total );

    //! Call to do radiative transfer
    void radiation_transport( const unsigned int& run );

  //================== Output Functions ================================//

    //! Call the output routines
    void generate_output( const unsigned int& run );

    //! Write hdf5 output file
    void write_hdf5_output(char* name, const unsigned int& run);

    //! calculate position of the I-Front for source in centre
    double calc_IFront( const unsigned int& run );

    //! calculate escape fraction
    double calc_escape_fraction( const unsigned int& run, const unsigned int& times );

    //================ Generic Functions ==============================//

    //! clear all temporary structures for the next run
    void clear_temporary();

    //! Function to give diagnostic output about total number of photons in simulation
    double count_photons();

    //! return number of runs
    const unsigned int& get_numRuns() const{ return numRuns; }

    //! Return number of outputs
    const unsigned int& get_numOutputs() const{ return numOutputs; }

    // ================ Public lists and variables ===================//

    //! List of vertices that will be used to perform triangulation
    vector< Vertex > vertices;

    //! List of sites for doing radiative transfer
    vector< Site > sites;

    //! Vector holding the domain decomposition
    vector< unsigned int > dom_dec;

    //! List of sites from the previous run, whose properties are still needed
    vector< Site_Update > site_properties;

    //! List of entries in site_intensities array of every site
    vector<unsigned long int> intens_ids;
    //! List of site intensities from previous run, that are still needed
    vector<float> site_intensities;

    //! List of indices of sites needed for transport along procs
    vector< unsigned long int > send_list;

    //! List of site indices that might be removed
    vector< unsigned long int > sites_marked_for_removal;
    //! List of sites and properties to be updated
    vector< Site_Remove > total_sites_marked_for_removal;

    //! List of simplices that results from the QHull call
    vector< Simpl > simplices;
    //! Temporary list of neutral number densities that are read in
    vector< float > temp_n_HI_list;
    //! Temporary list of ionised number densities that are read in
    vector< float > temp_n_HII_list;
    //! Temporary list of fluxes that are read in
    vector< float > temp_flux_list;
    //! Temporary list of internalEnergies that are read in
    vector< float > temp_u_list;
    //! Temporary list of dudt that are read in
    vector< float > temp_dudt_list;
    //! Temporary list of clumping factors that are read in
    vector< float > temp_clumping_list;
    //! Temporary list of metallicity
    vector<float> temp_metallicity_list;

    //! Cross section of hydrogen
    vector<double> cross_H;
    //! The photon excess energy used to heat the gas
    vector<double> photon_excess_energy;
    //! The elemental abundances of metals (and Helium)
    vector<double> abundances;
    //! Vector of cooling curves
    vector< cooling_curve > curves;

    //! Variable used by random number generator
    gsl_rng * ran;
    //! Output to logfile
    ofstream simpleXlog;

  protected:

    unsigned long int numSites;            //!< Total number of sites in the simulation
    unsigned long int origNumSites;        //!< Original number of sites
    unsigned int borderSites;      //!< Number of sites in the boundary around computational domain
    unsigned int hilbert_order;    //!< Hilbert order that determines the number of subboxes
    unsigned int numSweeps;        //!< Number of grid sweeps during simulation
    unsigned int numRuns;          //!< Number of runs of the simulation
    unsigned int numOutputs;       //!< Number of outputs of the simulation
    unsigned int numPixels;        //!< Number of pixels in HEALPix calculation
    unsigned int numStraight;      //!< Number of straight directions
    int randomSeed;                //!< Seed for random number generator
    short dimension;               //!< Dimension of the simulation (default = 3)
    string inputFileName;          //!< File which holds densities and fluxes to be read in
    string dataPath;               //!< Path where the metal line cooling tables are
    bool blackBody;                //!< Use blackbody for source spectrum?
    bool recombination;            //!< Include recombination or not
    bool rec_rad;                  //!< Include recombination radiation or not
    bool coll_ion;                 //!< Include collisional ionisations?
    bool heat_cool;                //!< Include heating and cooling?
    bool metal_cooling;            //!< Include metal line cooling?
    short int numFreq;             //!< Number of frequencies
    short int freq_spacing;        //!< Spacing of the frequency bins
    short RTmethod;                //!< Method for RT
    bool diffuseTransport;         //!< Set this to 1 to do only ballistic transport
    bool ballisticTransport;       //!< Set this to 1 to do only ballistic transport
    bool dirConsTransport;         //!< Set this to 1 to do only direction conserving transport
    bool combinedTransport;        //!< Set this to 1 to do combined transport
    bool photon_conservation;      //!< Use temporal photon conservation scheme or not?
    double subcycle_frac;          //!< Fraction of the characteristic time scale at which subcycling is done
    double sizeBox;                //!< Size of the box in parsec
    float borderBox;               //!< Size of boundary around computational domain
    float padding_subbox;          //!< Subbox boundary in which extra points are triangulated
    double simTime;                //!< Total simulation time in Myr
    double time_conversion;        //!< Time conversion for output
    double sourceTeff;             //!< Effective temperature of the sources in K
    double gasIonTemp;             //!< Temperature of the ionised gas in K
    double gasNeutralTemp;         //!< Temperature of the neutral gas in K
    double switchTau;              //!< Optical depth below which ballistic transport is no longer trusted
    double totalVolume;            //!< Total volume of all Voronoi cells added up
    double totalAtoms;             //!< Total number of atoms in the simulation domain
    int localMaxRes;               //!< Maximum resolution on the local processor
    unsigned int COMM_RANK;        //!< Rank of this proc
    unsigned int COMM_SIZE;        //!< Total number of procs
    unsigned int chunk_size;       //!< Maximum size of chunks written in one piece to hdf5 file
    unsigned int max_msg_to_send;  //!< Maximum number of messages to send in MPI call
    unsigned long int vertex_id_max;    //!< Maximum vertex index
    short int orientation_index;      //!< Orientation of the unit sphere tesselation
    short int orientation_index_old;  //!< Orientation of the unit sphere tesselation in previous run
    float euler_phi;               //!< Euler angle phi for random rotation of coordinate system
    float euler_theta;             //!< Euler angle theta for random rotation of coordinate system
    float euler_psi;               //!< Euler angle psi for random rotation of coordinate system
    bool heal_pix;                 //!< Use heal_pix for associating directions and neighbours or not?
    int num_ref_pix_HP;            //!< Number of pixels to use for associating neighbours in compute_solid_angles()
    float straightAngle;           //!< Maximum angle allowed between straight direction and Delaunay direction.
    bool straight_from_tess;       //!< In DCT, calculate straightest neighbours wrt Delaunay edge or direction bin?
    double UNIT_L;                 //!< Unit length in box
    double UNIT_M;                 //!< Unit mass in box, converts number density into number of atoms
    double UNIT_I;                 //!< Unit number of ionising photons in box
    double UNIT_T;                 //!< Time of 1 sweep in seconds
    double UNIT_T_MYR;             //!< Time of 1 sweep in Myr
    double UNIT_D;                 //!< Unit number density
    double UNIT_V;                 //!< Unit volume
    float straight_correction_factor; //!< Correction factor to correct for the fact that no straight lines exist on the grid
    vertex_tree vert_tree;         //!< Octree that will contain the vertices
    bool give_IFront;                   //!< Calculate IFront position for a source in centre?
    ofstream IFront_output;        //!< Output of IFront data to file
    float*** orient;               //!< Array to hold the direction bins
    unsigned int*** maps;          //!< Array to hold the mappings between direction bins
    unsigned int number_of_directions; //!< Number of discretizations of the unit sphere for DCT
    unsigned int number_of_orientations;//!< Number of random rotations of the DCT discretization
};

#endif


#pragma once
#include <vector>
#include <array>
#include <gsl/gsl>

#pragma warning(push)
#include <CppCoreCheck/Warnings.h>
#pragma warning(disable: ALL_CPPCORECHECK_WARNINGS)
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>
#pragma warning(pop)

#include <vulkan/vulkan.h>

#include "../utils/Utils.h"
#include "./RenderUtils.h"
#include "../Configuration.h"

#define VMA_USE_ALLOCATOR
#ifdef VMA_USE_ALLOCATOR
#pragma warning(push)
#include <CppCoreCheck/Warnings.h>
#pragma warning(disable: ALL_CPPCORECHECK_WARNINGS)
#include "vk_mem_alloc.h"
#pragma warning(pop)

/**
Our main unsigned in type to accomodate
vulkan's needs
*/
using uint = uint32_t;

/**
Wrapps a Vulkan Buffer with allocation information tied to it
*/
struct AllocatedBuffer {
	VkBuffer buffer{};
	VmaAllocation allocation{};
	VmaAllocationInfo allocation_info{};
};

/**
Wrapps a Vulkan Image with allocation information tied to it
*/
struct AllocatedImage {
	VkImage image{};
	VmaAllocation allocation{};
	VmaAllocationInfo allocation_info{};
};

#else

#pragma message ( "This program is meant to currently use VMA allocation" )
#error Current code needs to use VMA allocation

struct AllocatedBuffer {
	VkBuffer buffer{};
	VkDeviceMemory memory{};
};

struct AllocatedImage {
	VkImage image{};
	VkDeviceMemory memory{};
};

#endif

/**
Wraps a Vulkan Command Buffer with relevant
type and state information tied to it
*/
struct WrappedCommandBuffer {
	VkCommandBuffer buffer{};
	CommandType type{};
	bool recording{ false };
};

/**
Wraps a Vulkan Render Target with all the relevant information to
render to it like the image, memory, view, width, heigth and if it has
been initializated.

We are not using an "AllocatedImage" because allocation of a render target
is done with a custom method because of the special requirements of it being
a render target.
*/
struct WrappedRenderTarget {
	VkImage image{};
	VkDeviceMemory memory{};
	VkImageView view{};
	uint width{};
	uint heigth{};
	bool init{ false };
};

/**
Struct that holds dynamic configuration parameters of the renderer
*/
struct RenderConfiguration {
	short multisampling_samples{ config::initial_multisampling_samples };
};

struct Vertex {
	glm::vec3 pos{};
	glm::vec3 color{};
	glm::vec2 tex_coord{};

	auto static getBindingDescription() noexcept ->VkVertexInputBindingDescription {

		auto binding_description = VkVertexInputBindingDescription{};

		binding_description.binding = 0;
		binding_description.stride = sizeof(Vertex);
		binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

		return binding_description;
	}

	auto static getAttributeDescriptions() noexcept->std::array<VkVertexInputAttributeDescription, 3> {

		auto attribute_descriptions = std::array<VkVertexInputAttributeDescription, 3>{};

		attribute_descriptions[0].binding = 0;
		attribute_descriptions[0].location = 0;
		attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
		attribute_descriptions[0].offset = offsetof(Vertex, pos);

		attribute_descriptions[1].binding = 0;
		attribute_descriptions[1].location = 1;
		attribute_descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
		attribute_descriptions[1].offset = offsetof(Vertex, color);

		attribute_descriptions[2].binding = 0;
		attribute_descriptions[2].location = 2;
		attribute_descriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
		attribute_descriptions[2].offset = offsetof(Vertex, tex_coord);

		return attribute_descriptions;
	}

	auto operator==(const Vertex& other) const ->bool {
		return	pos == other.pos &&
				color == other.color &&
				tex_coord == other.tex_coord;
	}
};

namespace std {
	template<> struct hash<Vertex> {
		size_t operator()(Vertex const& vertex) const {
			return(
				(hash<glm::vec3>()(vertex.pos) ^
				(hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
				(hash<glm::vec2>()(vertex.tex_coord) << 1);
		}
	};
}

/*
MVP model.

http://www.opengl-tutorial.org/es/beginners-tutorials/tutorial-3-matrices/
https://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/
*/
struct UniformBufferObject {
	glm::mat4 model{};
	glm::mat4 view{};
	glm::mat4 proj{};
};

struct SimpleObjScene {
	std::vector<Vertex> vertices;
	std::vector<uint32_t> indices;
	AllocatedImage m_texture_image{};
	VkImageView m_texture_image_view{};
};

#if 0
#pragma warning(push)
#include <CppCoreCheck/Warnings.h>
#pragma warning(disable: 26426)
const auto vertices = std::vector<Vertex>{
	{ { -0.5f, -0.5f, 0.0f },{ 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f} },
	{ {  0.5f, -0.5f, 0.0f },{ 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } },
	{ {  0.5f,  0.5f, 0.0f },{ 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f } },
	{ { -0.5f,  0.5f, 0.0f },{ 1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f } },

	{ { -0.5f, -0.5f, -0.5f },{ 1.0f, 0.0f, 0.0f },{ 1.0f, 0.0f } },
	{ {  0.5f, -0.5f, -0.5f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f } },
	{ {  0.5f,  0.5f, -0.5f },{ 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f } },
	{ { -0.5f,  0.5f, -0.5f },{ 1.0f, 1.0f, 1.0f },{ 1.0f, 1.0f } },
};

const auto indices = std::vector<uint16_t>{
	0, 1, 2, 2, 3, 0,
	4, 5, 6, 6, 7, 4
};
#pragma warning(pop)
#endif


//**************************************************************************\
//* This file is property of and copyright by the ALICE Project            *\
//* ALICE Experiment at CERN, All rights reserved.                         *\
//*                                                                        *\
//* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *\
//*                  for The ALICE HLT Project.                            *\
//*                                                                        *\
//* Permission to use, copy, modify and distribute this software and its   *\
//* documentation strictly for non-commercial purposes is hereby granted   *\
//* without fee, provided that the above copyright notice appears in all   *\
//* copies and that both the copyright notice and this permission notice   *\
//* appear in the supporting documentation. The authors make no claims     *\
//* about the suitability of this software for any purpose. It is          *\
//* provided "as is" without express or implied warranty.                  *\
//**************************************************************************

/// \file GPUQA.h
/// \author David Rohr

#ifndef GPUQA_H
#define GPUQA_H

#include "GPUSettings.h"
struct AliHLTTPCClusterMCWeight;
class TH1F;
class TH2F;
class TCanvas;
class TPad;
class TLegend;
class TPad;
class TH1;
class TFile;
class TH1D;
class TObjArray;
class TColor;
typedef short int Color_t;

#if !defined(GPUCA_BUILD_QA) || defined(GPUCA_GPUCODE)

namespace GPUCA_NAMESPACE
{
namespace gpu
{
class GPUChainTracking;

class GPUQA
{
 public:
  GPUQA(GPUChainTracking* chain) {}
  ~GPUQA() = default;

  int InitQA(int tasks = 0) { return 1; }
  void RunQA(bool matchOnly = false) {}
  int DrawQAHistograms() { return 1; }
  void SetMCTrackRange(int min, int max) {}
  bool SuppressTrack(int iTrack) const { return false; }
  bool SuppressHit(int iHit) const { return false; }
  int HitAttachStatus(int iHit) const { return false; }
  int GetMCTrackLabel(unsigned int trackId) const { return -1; }
  bool clusterRemovable(int attach, bool prot) const { return false; }
  static bool QAAvailable() { return false; }
  static bool IsInitialized() { return false; }
};
} // namespace gpu
} // namespace GPUCA_NAMESPACE

#else

#include "GPUTPCDef.h"
#include <cmath>
#include <vector>
#include <memory>
#ifdef GPUCA_TPC_GEOMETRY_O2
#include <gsl/span>
#endif

namespace o2
{
class MCCompLabel;
namespace tpc
{
class TrackTPC;
struct ClusterNativeAccess;
} // namespace tpc
} // namespace o2

struct AliHLTTPCClusterMCLabel;

namespace GPUCA_NAMESPACE::gpu
{
class GPUChainTracking;
class GPUParam;
struct GPUTPCMCInfo;
struct GPUQAGarbageCollection;

class GPUQA
{
 public:
  GPUQA();
  GPUQA(GPUChainTracking* chain, const GPUSettingsQA* config = nullptr, const GPUParam* param = nullptr);
  ~GPUQA();

  int InitQA(int tasks = -1);
  void RunQA(bool matchOnly = false, const std::vector<o2::tpc::TrackTPC>* tracksExternal = nullptr, const std::vector<o2::MCCompLabel>* tracksExtMC = nullptr, const o2::tpc::ClusterNativeAccess* clNative = nullptr);
  int DrawQAHistograms(TObjArray* qcout = nullptr);
  void DrawQAHistogramsCleanup(); // Needed after call to DrawQAHistograms with qcout != nullptr when GPUSettingsQA.shipToQCAsCanvas = true to clean up the Canvases etc.
  void SetMCTrackRange(int min, int max);
  bool SuppressTrack(int iTrack) const;
  bool SuppressHit(int iHit) const;
  int HitAttachStatus(int iHit) const;
  int GetMCTrackLabel(unsigned int trackId) const;
  bool clusterRemovable(int attach, bool prot) const;
  static bool QAAvailable() { return true; }
  bool IsInitialized() { return mQAInitialized; }

  const std::vector<TH1F>& getHistograms1D() const { return *mHist1D; }
  const std::vector<TH2F>& getHistograms2D() const { return *mHist2D; }
  const std::vector<TH1D>& getHistograms1Dd() const { return *mHist1Dd; }
  void resetHists();
  int loadHistograms(std::vector<TH1F>& i1, std::vector<TH2F>& i2, std::vector<TH1D>& i3, int tasks = -1);

  static constexpr int N_CLS_HIST = 8;
  static constexpr int N_CLS_TYPE = 3;

  static constexpr int MC_LABEL_INVALID = -1e9;

  enum QA_TASKS {
    taskTrackingEff = 1,
    taskTrackingRes = 2,
    taskTrackingResPull = 4,
    taskClusterAttach = 8,
    taskTrackStatistics = 16,
    taskClusterCounts = 32,
    taskDefault = 63,
    taskDefaultPostprocess = 31
  };

 private:
  struct additionalMCParameters {
    float pt, phi, theta, eta, nWeightCls;
  };

  struct additionalClusterParameters {
    int attached, fakeAttached, adjacent, fakeAdjacent;
    float pt;
  };

  int InitQACreateHistograms();
  int DoClusterCounts(unsigned long long int* attachClusterCounts, int mode = 0);
  void PrintClusterCount(int mode, int& num, const char* name, unsigned long long int n, unsigned long long int normalization);

  void SetAxisSize(TH1F* e);
  void SetLegend(TLegend* l);
  double* CreateLogAxis(int nbins, float xmin, float xmax);
  void ChangePadTitleSize(TPad* p, float size);
  void DrawHisto(TH1* histo, char* filename, char* options);
  void doPerfFigure(float x, float y, float size);
  void GetName(char* fname, int k);
  template <class T>
  T* GetHist(T*& ee, std::vector<std::unique_ptr<TFile>>& tin, int k, int nNewInput);

#ifdef GPUCA_TPC_GEOMETRY_O2
  using mcLabels_t = gsl::span<const o2::MCCompLabel>;
  using mcLabel_t = o2::MCCompLabel;
  using mcLabelI_t = mcLabel_t;
  using mcInfo_t = GPUTPCMCInfo;
  mcLabels_t GetMCLabel(unsigned int i);
  mcLabel_t GetMCLabel(unsigned int i, unsigned int j);
#else
  using mcLabels_t = AliHLTTPCClusterMCLabel;
  using mcLabel_t = AliHLTTPCClusterMCWeight;
  struct mcLabelI_t {
    int getTrackID() const { return AbsLabelID(track); }
    int getEventID() const { return 0; }
    long int getTrackEventSourceID() const { return getTrackID(); }
    bool isFake() const { return track < 0; }
    bool isValid() const { return track != MC_LABEL_INVALID; }
    void invalidate() { track = MC_LABEL_INVALID; }
    void setFakeFlag(bool v = true) { track = v ? FakeLabelID(track) : AbsLabelID(track); }
    void setNoise() { track = MC_LABEL_INVALID; }
    bool operator==(const mcLabel_t& l);
    bool operator!=(const mcLabel_t& l) { return !(*this == l); }
    mcLabelI_t() = default;
    mcLabelI_t(const mcLabel_t& l);
    int track = MC_LABEL_INVALID;
  };
  using mcInfo_t = GPUTPCMCInfo;
  const mcLabels_t& GetMCLabel(unsigned int i);
  const mcLabel_t& GetMCLabel(unsigned int i, unsigned int j);
  const mcInfo_t& GetMCTrack(const mcLabelI_t& label);
  static int FakeLabelID(const int id);
  static int AbsLabelID(const int id);
#endif
  template <class T>
  static auto& GetMCTrackObj(T& obj, const mcLabelI_t& l);

  unsigned int GetNMCCollissions();
  unsigned int GetNMCTracks(int iCol);
  unsigned int GetNMCLabels();
  const mcInfo_t& GetMCTrack(unsigned int iTrk, unsigned int iCol);
  const mcInfo_t& GetMCTrack(const mcLabel_t& label);
  int GetMCLabelNID(const mcLabels_t& label);
  int GetMCLabelNID(unsigned int i);
  int GetMCLabelID(unsigned int i, unsigned int j);
  int GetMCLabelCol(unsigned int i, unsigned int j);
  static int GetMCLabelID(const mcLabels_t& label, unsigned int j);
  static int GetMCLabelID(const mcLabel_t& label);
  float GetMCLabelWeight(unsigned int i, unsigned int j);
  float GetMCLabelWeight(const mcLabels_t& label, unsigned int j);
  float GetMCLabelWeight(const mcLabel_t& label);
  const auto& GetClusterLabels();
  bool mcPresent();

  static bool MCComp(const mcLabel_t& a, const mcLabel_t& b);

  GPUChainTracking* mTracking;
  const GPUSettingsQA& mConfig;
  const GPUParam& mParam;

  const char* str_perf_figure_1 = "ALICE Performance 2018/03/20";
  // const char* str_perf_figure_2 = "2015, MC pp, #sqrt{s} = 5.02 TeV";
  const char* str_perf_figure_2 = "2015, MC Pb-Pb, #sqrt{s_{NN}} = 5.02 TeV";
  //-------------------------

  std::vector<mcLabelI_t> mTrackMCLabels;
#ifdef GPUCA_TPC_GEOMETRY_O2
  std::vector<std::vector<int>> mTrackMCLabelsReverse;
  std::vector<std::vector<int>> mRecTracks;
  std::vector<std::vector<int>> mFakeTracks;
  std::vector<std::vector<additionalMCParameters>> mMCParam;
  std::vector<std::vector<mcInfo_t>> mMCInfos;
  std::vector<int> mNColTracks;
#else
  std::vector<int> mTrackMCLabelsReverse[1];
  std::vector<int> mRecTracks[1];
  std::vector<int> mFakeTracks[1];
  std::vector<additionalMCParameters> mMCParam[1];
#endif
  std::vector<additionalClusterParameters> mClusterParam;
  int mNTotalFakes = 0;

  TH1F* mEff[4][2][2][5][2]; // eff,clone,fake,all - findable - secondaries - y,z,phi,eta,pt - work,result
  TCanvas* mCEff[6];
  TPad* mPEff[6][4];
  TLegend* mLEff[6];

  TH1F* mRes[5][5][2]; // y,z,phi,lambda,pt,ptlog res - param - res,mean
  TH2F* mRes2[5][5];
  TCanvas* mCRes[7];
  TPad* mPRes[7][5];
  TLegend* mLRes[6];

  TH1F* mPull[5][5][2]; // y,z,phi,lambda,pt,ptlog res - param - res,mean
  TH2F* mPull2[5][5];
  TCanvas* mCPull[7];
  TPad* mPPull[7][5];
  TLegend* mLPull[6];

  enum CL_types { CL_attached = 0,
                  CL_fake = 1,
                  CL_att_adj = 2,
                  CL_fakeAdj = 3,
                  CL_tracks = 4,
                  CL_physics = 5,
                  CL_prot = 6,
                  CL_all = 7 };
  TH1D* mClusters[N_CLS_TYPE * N_CLS_HIST - 1]; // attached, fakeAttached, attach+adjacent, fakeAdjacent, physics, protected, tracks, all / count, rel, integral
  TCanvas* mCClust[N_CLS_TYPE];
  TPad* mPClust[N_CLS_TYPE];
  TLegend* mLClust[N_CLS_TYPE];

  struct counts_t {
    long long int nRejected = 0, nTube = 0, nTube200 = 0, nLoopers = 0, nLowPt = 0, n200MeV = 0, nPhysics = 0, nProt = 0, nUnattached = 0, nTotal = 0, nHighIncl = 0, nAbove400 = 0, nFakeRemove400 = 0, nFullFakeRemove400 = 0, nBelow40 = 0, nFakeProtect40 = 0, nMergedLooper = 0;
    double nUnaccessible = 0;
  } mClusterCounts;

  TH1F* mTracks;
  TCanvas* mCTracks;
  TPad* mPTracks;
  TLegend* mLTracks;

  TH1F* mNCl;
  TCanvas* mCNCl;
  TPad* mPNCl;
  TLegend* mLNCl;

  std::vector<TH2F*> mHistClusterCount;

  std::vector<TH1F>* mHist1D = nullptr;
  std::vector<TH2F>* mHist2D = nullptr;
  std::vector<TH1D>* mHist1Dd = nullptr;
  bool mHaveExternalHists = false;
  std::vector<TH1F**> mHist1D_pos{};
  std::vector<TH2F**> mHist2D_pos{};
  std::vector<TH1D**> mHist1Dd_pos{};
  template <class T>
  auto getHistArray();
  template <class T, typename... Args>
  void createHist(T*& h, const char* name, Args... args);

  std::unique_ptr<GPUQAGarbageCollection> mGarbageCollector;
  template <class T, typename... Args>
  T* createGarbageCollected(Args... args);
  void clearGarbagageCollector();

  int mNEvents = 0;
  bool mQAInitialized = false;
  int mQATasks = 0;
  std::vector<std::vector<int>> mcEffBuffer;
  std::vector<std::vector<int>> mcLabelBuffer;
  std::vector<std::vector<bool>> mGoodTracks;
  std::vector<std::vector<bool>> mGoodHits;

  static std::vector<TColor*> mColors;
  static int initColors();

  int mMCTrackMin = -1, mMCTrackMax = -1;

  const o2::tpc::ClusterNativeAccess* mClNative = nullptr;
};

inline bool GPUQA::SuppressTrack(int iTrack) const { return (mConfig.matchMCLabels.size() && !mGoodTracks[mNEvents][iTrack]); }
inline bool GPUQA::SuppressHit(int iHit) const { return (mConfig.matchMCLabels.size() && !mGoodHits[mNEvents - 1][iHit]); }
inline int GPUQA::HitAttachStatus(int iHit) const { return (mClusterParam.size() && mClusterParam[iHit].fakeAttached ? (mClusterParam[iHit].attached ? 1 : 2) : 0); }

} // namespace GPUCA_NAMESPACE::gpu

#endif
#endif


#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
/*
 * ocl_model_maintenance.c
 *
 *  Created on: 09.02.2014
 *      Author: mheimel
 */

#include "ocl_model_maintenance.h"

#include <math.h>
#include <stdlib.h>
#include <sys/time.h>

#include "ocl_adaptive_bandwidth.h"
#include "ocl_error_metrics.h"
#include "ocl_estimator.h"
#include "ocl_sample_maintenance.h"
#include "ocl_utilities.h"

// Optimization routines.
#include <nlopt.h>
#include "lbfgs/lbfgs.h"

#include "catalog/pg_kdefeedback.h"
#include "executor/spi.h"
#include "optimizer/path/gpukde/ocl_estimator_api.h"
#include "storage/lock.h"

// Global GUC variables
bool kde_enable_bandwidth_optimization;
int kde_bandwidth_optimization_feedback_window;
extern int kde_bandwidth_representation;

const double learning_rate = 0.01f;

// ############################################################
// # Code for adaptive bandwidth optimization (online learning).
// ############################################################

void ocl_notifyModelMaintenanceOfSelectivity(
    Oid relation, double selected, double allrows) {
  CREATE_TIMER();

  // Check if we have an estimator for this relation.
  ocl_estimator_t* estimator = ocl_getEstimator(relation);
  if (estimator == NULL) return;
  if (!estimator->open_estimation) return;  // No registered estimation.

  double selectivity = selected / allrows;
  estimator->rows_in_table = allrows;

  // Notify the sample maintenance of this observation so it can track the sample quality.
  ocl_notifySampleMaintenanceOfSelectivity(estimator, selectivity);

  // Update the bandwidth using online learning.
  ocl_runOnlineLearningStep(estimator, selectivity);

  // Write the error to the log file.
  ocl_reportErrorToLogFile(
      relation, estimator->last_selectivity, selectivity,
      estimator->rows_in_table);

  // We are done.
  estimator->open_estimation = false;
  LOG_TIMER("Model Maintenance");
}

// ############################################################
// # Code for offline bandwidth optimization (batch learning).
// ############################################################

// Helper function to extract the n latest feedback records for the given
// estimator from the catalogue. The function will only return tuples that
// have feedback that matches the estimator's attributes.
//
// Returns the actual number of valid feedback records in the catalog.
static unsigned int ocl_extractLatestFeedbackRecordsFromCatalog(
    ocl_estimator_t* estimator, unsigned int requested_records,
    kde_float_t* range_buffer, kde_float_t* selectivity_buffer) {
  unsigned int current_tuple = 0;

  // Open a new scan over the feedback table.
  unsigned int i, j;
  if (SPI_connect() != SPI_OK_CONNECT) {
    fprintf(stderr, "> Error connecting to Postgres Backend.\n");
    return current_tuple;
  }
  char query_buffer[1024];
  snprintf(query_buffer, 1024,
           "SELECT columns, ranges, alltuples, qualifiedtuples FROM "
           "pg_kdefeedback WHERE \"table\" = %i ORDER BY \"timestamp\" DESC "
           "LIMIT %i;", estimator->table, requested_records);
  if (SPI_execute(query_buffer, true, 0) != SPI_OK_SELECT) {
    fprintf(stderr, "> Error querying system table.\n");
    return current_tuple;
  }
  SPITupleTable *spi_tuptable = SPI_tuptable;
  unsigned int result_tuples = SPI_processed;
  TupleDesc spi_tupdesc = spi_tuptable->tupdesc;
  for (i = 0; i < result_tuples; ++i) {
    bool isnull;
    HeapTuple record_tuple = spi_tuptable->vals[i];
    // First, check whether this record only covers columns that are part of the estimator.
    unsigned int columns_in_record = DatumGetInt32(
          SPI_getbinval(record_tuple, spi_tupdesc, 1, &isnull));
    if ((columns_in_record | estimator->columns) != estimator->columns) continue;
    // This is a valid record, initialize the range buffer.
    unsigned int pos = current_tuple * 2 * estimator->nr_of_dimensions;
    for (j=0; j<estimator->nr_of_dimensions; ++j) {
      range_buffer[pos + 2*j] = -1.0 * INFINITY;
      range_buffer[pos + 2*j + 1] = INFINITY;
    }
    // Now extract all clauses and isert them into the range buffer.
    RQClause* clauses;
    unsigned int nr_of_clauses = extract_clauses_from_buffer(DatumGetByteaP(
        SPI_getbinval(record_tuple, spi_tupdesc, 2, &isnull)), &clauses);
    for (j=0; j<nr_of_clauses; ++j) {
      // First, locate the correct column position in the estimator.
      int column_in_estimator = estimator->column_order[clauses[j].var];
      // Re-Scale the bounds, add potential padding and write them to their position.
      float8 lo = clauses[j].lobound;
      if (clauses[j].loinclusive != EX) lo -= 0.001;
      float8 hi = clauses[j].hibound;
      if (clauses[j].hiinclusive != EX) hi += 0.001;
      range_buffer[pos + 2*column_in_estimator] = lo;
      range_buffer[pos + 2*column_in_estimator + 1] = hi;
    }
    // Finally, extract the selectivity and increase the tuple count.
    double all_rows = DatumGetFloat8(
        SPI_getbinval(record_tuple, spi_tupdesc, 3, &isnull));
    double qualified_rows = DatumGetFloat8(
        SPI_getbinval(record_tuple, spi_tupdesc, 4, &isnull));
    selectivity_buffer[current_tuple++] = qualified_rows / all_rows;
  }
  // We are done :)
  SPI_finish();
  return current_tuple;
}

// Helper function that extracts feedback for the given estimator and
// pushes it to the device.
static unsigned int ocl_prepareFeedback(
    ocl_estimator_t* estimator, cl_mem* device_ranges,
    cl_mem* device_selectivities) {
  cl_int err = CL_SUCCESS;
  // First, we have to count how many matching feedback records are available
  // for this estimator in the query feedback table.
  if (SPI_connect() != SPI_OK_CONNECT) {
    fprintf(stderr, "> Error connecting to Postgres Backend.\n");
    return 0;
  }
  char query_buffer[1024];
  snprintf(query_buffer, 1024,
           "SELECT COUNT(*) FROM pg_kdefeedback WHERE \"table\" = %i;",
           estimator->table);
  if (SPI_execute(query_buffer, true, 0) != SPI_OK_SELECT) {
    fprintf(stderr, "> Error querying system table.\n");
    return 0;
  }
  SPITupleTable *tuptable = SPI_tuptable;
  bool isnull;
  unsigned int available_records = DatumGetInt32(
      SPI_getbinval(tuptable->vals[0], tuptable->tupdesc, 1, &isnull));
  SPI_finish();

  if (available_records == 0) {
    fprintf(stderr, "> No feedback available for table %i\n", estimator->table);
    return 0;
  }

  // Adjust the number of records according to the specified window size.
  int used_records;
  if (kde_bandwidth_optimization_feedback_window == -1)
    used_records = available_records;
  else
    used_records = Min(available_records,
                       kde_bandwidth_optimization_feedback_window);
  fprintf(stderr, "> Checking the %i latest feedback records.\n", used_records);

  // Allocate arrays and fetch the actual feedback data.
  kde_float_t* range_buffer = palloc(
      sizeof(kde_float_t) * 2 * estimator->nr_of_dimensions * used_records);
  kde_float_t* selectivity_buffer = palloc(sizeof(kde_float_t) * used_records);
  unsigned int actual_records = ocl_extractLatestFeedbackRecordsFromCatalog(
      estimator, used_records, range_buffer, selectivity_buffer);

  // Now push the records to the device to prepare the actual optimization.
  if (actual_records == 0) {
    fprintf(stderr, "> No valid feedback records found.\n");
    return 0;
  }
  fprintf(stderr, "> Found %i valid records, pushing to device.\n",
          actual_records);
  ocl_context_t* context = ocl_getContext();
  *device_ranges = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE,
      sizeof(kde_float_t) * 2 * actual_records * estimator->nr_of_dimensions,
      NULL, &err);
  Assert(err == CL_SUCCESS);

  err = clEnqueueWriteBuffer(
      context->queue, *device_ranges, CL_FALSE, 0,
      sizeof(kde_float_t) * 2 * actual_records * estimator->nr_of_dimensions,
      range_buffer, 0, NULL, NULL);
  estimator->stats->optimization_transfer_to_device++;
  Assert(err == CL_SUCCESS);

  *device_selectivities = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE,
      sizeof(kde_float_t) * actual_records, NULL, &err);
  Assert(err == CL_SUCCESS);

  err = clEnqueueWriteBuffer(
      context->queue, *device_selectivities, CL_FALSE, 0,
      sizeof(kde_float_t) * actual_records, selectivity_buffer, 0, NULL, NULL);
  estimator->stats->optimization_transfer_to_device++;
  Assert(err == CL_SUCCESS);

  err = clFinish(context->queue);
  Assert(err == CL_SUCCESS);

  pfree(range_buffer);
  pfree(selectivity_buffer);
  return actual_records;
}

typedef struct {
  ocl_estimator_t* estimator;
  unsigned int nr_of_observations;
  cl_mem observed_ranges;
  cl_mem observed_selectivities;
  // Temporary buffers.
  size_t stride_size;
  cl_mem gradient_accumulator_buffer;
  cl_mem error_accumulator_buffer;
  cl_mem gradient_buffer;
  cl_mem error_buffer;
  // Required event and accumulation buffers.
  ocl_aggregation_descriptor_t** summation_descriptors;
  cl_mem* summation_buffers;
} optimization_config_t;

/**
 * Counters for evaluations.
 */
int evaluations;
double start_error;
struct timeval opt_start;

/*
 * Function to compute the gradient for a penalized objective function that will
 * add a strong penalty factor to negative bandwidth values. This function is
 * passed to nlopt to compute the gradient and evaluate the function.
 */

/*
 * Callback function that computes the gradient and value for the objective
 * function at the current bandwidth.
 */
static double computeGradient(
    unsigned n, const double* bandwidth, double* gradient, void* params) {
  unsigned int i;
  cl_int err = CL_SUCCESS;
  optimization_config_t* conf = (optimization_config_t*)params;
  ocl_estimator_t* estimator = conf->estimator;
  ocl_context_t* context = ocl_getContext();

  evaluations++;

  if (ocl_isDebug()) {
    fprintf(stderr, ">>> Evaluation %i:\n\tCurrent bandwidth:", evaluations);
    for (i=0; i<n; ++i) fprintf(stderr, " %e", bandwidth[i]);
    fprintf(stderr, "\n");
  }

  // First, transfer the current bandwidth to the device. Note that we might
  // need to cast the bandwidth to float first.
  kde_float_t* fbandwidth = NULL;
  cl_event input_transfer_event;
  if (sizeof(kde_float_t) != sizeof(double)) {
    fbandwidth = palloc(sizeof(kde_float_t) * estimator->nr_of_dimensions);
    for (i = 0; i<estimator->nr_of_dimensions; ++i) {
      fbandwidth[i] = bandwidth[i];
    }
    err = clEnqueueWriteBuffer(
        context->queue, estimator->bandwidth_buffer, CL_FALSE,
        0, sizeof(kde_float_t) * estimator->nr_of_dimensions,
        fbandwidth, 0, NULL, &input_transfer_event);
    estimator->stats->optimization_transfer_to_device++;
    Assert(err == CL_SUCCESS);
  } else {
    err = clEnqueueWriteBuffer(
        context->queue, estimator->bandwidth_buffer, CL_FALSE,
        0, sizeof(kde_float_t) * estimator->nr_of_dimensions,
        bandwidth, 0, NULL, &input_transfer_event);
    estimator->stats->optimization_transfer_to_device++;
    Assert(err == CL_SUCCESS);
  }
  // Prepare the kernel that computes a gradient for each observation.
  cl_kernel gradient_kernel = ocl_getKernel(
      ocl_getSelectedErrorMetric()->batch_kernel_name,
      estimator->nr_of_dimensions);
    // First, fix the local and global size. We identify the optimal local size
    // by looking at available and required local memory and by ensuring that
    // the local size is evenly divisible by the preferred workgroup multiple..
  size_t local_size;
  err = clGetKernelWorkGroupInfo(
      gradient_kernel, context->device, CL_KERNEL_WORK_GROUP_SIZE,
      sizeof(size_t), &local_size, NULL);
  Assert(err == CL_SUCCESS);

  size_t available_local_memory;
  err = clGetKernelWorkGroupInfo(
      gradient_kernel, context->device, CL_KERNEL_LOCAL_MEM_SIZE,
      sizeof(size_t), &available_local_memory, NULL);
  Assert(err == CL_SUCCESS);

  available_local_memory = context->local_mem_size - available_local_memory;
  local_size = Min(
      local_size,
      available_local_memory / (3 * sizeof(kde_float_t) * estimator->nr_of_dimensions));
  size_t preferred_local_size_multiple;
  err = clGetKernelWorkGroupInfo(
      gradient_kernel, context->device,
      CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE,
      sizeof(size_t), &preferred_local_size_multiple, NULL);
  Assert(err == CL_SUCCESS);

  local_size = preferred_local_size_multiple
      * (local_size / preferred_local_size_multiple);
  size_t global_size = local_size * (conf->nr_of_observations / local_size);
  if (global_size < conf->nr_of_observations) global_size += local_size;
    // Configure the kernel by setting all required parameters.
  err |= clSetKernelArg(
      gradient_kernel, 0, sizeof(cl_mem), &(estimator->sample_buffer));
  err |= clSetKernelArg(
      gradient_kernel, 1, sizeof(unsigned int), &(estimator->rows_in_sample));
  err |= clSetKernelArg(
      gradient_kernel, 2, sizeof(cl_mem), &(conf->observed_ranges));
  err |= clSetKernelArg(
      gradient_kernel, 3, sizeof(cl_mem), &(conf->observed_selectivities));
  err |= clSetKernelArg(
      gradient_kernel, 4, sizeof(unsigned int), &(conf->nr_of_observations));
  err |= clSetKernelArg(
      gradient_kernel, 5, sizeof(cl_mem), &(estimator->bandwidth_buffer));
  err |= clSetKernelArg(
      gradient_kernel, 6,
      local_size * sizeof(kde_float_t) * estimator->nr_of_dimensions, NULL);
  err |= clSetKernelArg(
      gradient_kernel, 7,
      local_size * sizeof(kde_float_t) * estimator->nr_of_dimensions, NULL);
  err |= clSetKernelArg(
      gradient_kernel, 8,
      local_size * sizeof(kde_float_t) * estimator->nr_of_dimensions, NULL);
  err |= clSetKernelArg(
      gradient_kernel, 9, sizeof(cl_mem), &(conf->error_accumulator_buffer));
  err |= clSetKernelArg(
      gradient_kernel, 10, sizeof(cl_mem),
      &(conf->gradient_accumulator_buffer));
  unsigned int stride_elements = conf->stride_size / sizeof(kde_float_t);
  err |= clSetKernelArg(
      gradient_kernel, 11, sizeof(unsigned int), &stride_elements);
  err |= clSetKernelArg(
      gradient_kernel, 12, sizeof(unsigned int), &(estimator->rows_in_table));
  err |= clSetKernelArg(
      gradient_kernel, 13, sizeof(cl_mem), &(estimator->mean_buffer));
  err |= clSetKernelArg(
      gradient_kernel, 14, sizeof(cl_mem), &(estimator->sdev_buffer));
  Assert(err == CL_SUCCESS);

  // Compute the gradient for each observation.
  cl_event partial_gradient_event;
  err = clEnqueueNDRangeKernel(
      context->queue, gradient_kernel, 1, NULL, &global_size, &local_size, 1,
      &input_transfer_event, &partial_gradient_event);
  Assert(err == CL_SUCCESS);

  // Sum up the individual error contributions ...
  cl_event* summation_events = palloc(
      sizeof(cl_event) * (1 + estimator->nr_of_dimensions));

  summation_events[0] = predefinedSumOfArray(
      conf->summation_descriptors[0], partial_gradient_event);
  // .. and the individual gradients.
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    summation_events[i + 1] = predefinedSumOfArray(
        conf->summation_descriptors[i + 1], partial_gradient_event);
  }
  // Now transfer the gradient back to the device.
  cl_event result_events[2];
  kde_float_t* tmp_gradient = palloc(
      sizeof(kde_float_t) * estimator->nr_of_dimensions);
  err = clEnqueueReadBuffer(
      context->queue, conf->gradient_buffer, CL_FALSE,
      0, sizeof(kde_float_t) * estimator->nr_of_dimensions,
      tmp_gradient, estimator->nr_of_dimensions + 1,
      summation_events, &(result_events[0]));
  estimator->stats->optimization_transfer_to_host++;
  Assert(err == CL_SUCCESS);

  // As well as the error.
  kde_float_t error;
  err = clEnqueueReadBuffer(
      context->queue, conf->error_buffer, CL_FALSE,
      0, sizeof(kde_float_t), &error,
      1, &(summation_events[0]),  &(result_events[1]));
  estimator->stats->optimization_transfer_to_host++;
  Assert(err == CL_SUCCESS);

  err = clWaitForEvents(2, result_events);
  Assert(err == CL_SUCCESS);

  if (err != 0) {
    fprintf(stderr, "OpenCL functions failed to compute gradient.\n");
  }

  error /= conf->nr_of_observations;
  if (evaluations == 1) start_error = error;
  struct timeval now; gettimeofday(&now, NULL);
  long seconds = now.tv_sec - opt_start.tv_sec;
  long useconds = now.tv_usec - opt_start.tv_usec;
  long mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;
  // Ok, cool. Transfer the bandwidth back.
  if (ocl_isDebug()) {
    fprintf(
        stderr, "\r\tOptimization round %i. Current error: %f "
        "(started at %f), took: %ld ms.",
        evaluations, error, start_error, mtime);
  }
  // Finally, cast back to double.
  if (gradient) {
    for (i = 0; i<estimator->nr_of_dimensions; ++i) {
    // Apply the gradient normalization.
      double h = bandwidth[i];
      if(kde_bandwidth_representation == PLAIN_BW){
          gradient[i] = tmp_gradient[i] * M_SQRT2 / (
              sqrt(M_PI) * h * h * pow(2.0, estimator->nr_of_dimensions) *
              conf->nr_of_observations * estimator->rows_in_sample);
      }
      else {
          gradient[i] = tmp_gradient[i] * M_SQRT2 / (
              sqrt(M_PI) * exp(h) * pow(2.0, estimator->nr_of_dimensions) *
              conf->nr_of_observations * estimator->rows_in_sample);
      }
    }
    if (ocl_isDebug()) {
      fprintf(stderr, "\n\tGradient:");
      for (i=0; i<n; ++i) fprintf(stderr, " %e", gradient[i]);
      fprintf(stderr, "\n");
    }
  }
  // Ok, clean everything up.
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    err = clReleaseEvent(summation_events[i]);
    Assert(err == CL_SUCCESS);
  }
  pfree(summation_events);
  pfree(tmp_gradient);
  if (fbandwidth) pfree(fbandwidth);
  err |= clReleaseEvent(input_transfer_event);
  err |= clReleaseEvent(partial_gradient_event);
  err |= clReleaseEvent(result_events[0]);
  err |= clReleaseEvent(result_events[1]);
  err |= clReleaseKernel(gradient_kernel);
  Assert(err == CL_SUCCESS);

  return error;
}

static void ocl_setScottsBandwidth(ocl_estimator_t* estimator) {
  ocl_context_t* context = ocl_getContext();
  cl_int err = CL_SUCCESS;
  // First, we need to compute the variance for each dimension.
  unsigned int i;
  kde_float_t* variances = malloc(
      sizeof(kde_float_t) * estimator->nr_of_dimensions);
  cl_mem* buffers = malloc(sizeof(cl_mem) * estimator->nr_of_dimensions);
  cl_mem averages = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE,
      sizeof(kde_float_t) * estimator->nr_of_dimensions, NULL, &err);
  Assert(err == CL_SUCCESS);
  cl_event* events = malloc(sizeof(cl_event) * estimator->nr_of_dimensions);
  size_t sample_size = estimator->rows_in_sample;
  size_t dimensions = estimator->nr_of_dimensions;
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    // Allocate all required buffers.
    buffers[i] = clCreateBuffer(
        context->context, CL_MEM_READ_WRITE,
        sizeof(kde_float_t) * estimator->rows_in_sample, NULL, &err);
    Assert(err == CL_SUCCESS);
    // First we extract all sample components for this dimension.
    cl_kernel extractComponents = ocl_getKernel("extract_dimension", 0);
    err |= clSetKernelArg(
        extractComponents, 0, sizeof(cl_mem), &(estimator->sample_buffer));
    err |= clSetKernelArg(extractComponents, 1, sizeof(cl_mem), &(buffers[i]));
    err |= clSetKernelArg(extractComponents, 2, sizeof(unsigned int), &i);
    err |= clSetKernelArg(extractComponents, 3, sizeof(unsigned int),
        &(estimator->nr_of_dimensions));
    Assert(err == CL_SUCCESS);

    cl_event extraction_event;
    err = clEnqueueNDRangeKernel(
        context->queue, extractComponents, 1, NULL, &sample_size, NULL,
        0, NULL, &extraction_event);
    Assert(err == CL_SUCCESS);
    // Now we sum them up, so we can compute the average.
    cl_event average_summation_event = sumOfArray(
        buffers[i], estimator->rows_in_sample, averages, i, extraction_event);
    // Alright, we can compute the variance contributions from each point.
    cl_kernel precomputeVariance = ocl_getKernel("precompute_variance", 0);
    err |= clSetKernelArg(precomputeVariance, 0, sizeof(cl_mem), &(buffers[i]));
    err |= clSetKernelArg(precomputeVariance, 1, sizeof(cl_mem), &averages);
    err |= clSetKernelArg(precomputeVariance, 2, sizeof(unsigned int), &i);
    err |= clSetKernelArg(precomputeVariance, 3, sizeof(unsigned int),
        &(estimator->rows_in_sample));
    cl_event variance_event;
    err = clEnqueueNDRangeKernel(
        context->queue, precomputeVariance, 1, NULL, &sample_size, NULL,
        1, &average_summation_event, &variance_event);
    Assert(err == CL_SUCCESS);

    // We now sum up the single contributions to compute the variance.
    cl_event variance_summation_event = sumOfArray(
        buffers[i], estimator->rows_in_sample, averages, i, variance_event);
    // Finally, we can compute and store the bandwidth for this value.
    cl_kernel finalizeBandwidth = ocl_getKernel("set_scotts_bandwidth", 0);
    err |= clSetKernelArg(finalizeBandwidth, 0, sizeof(cl_mem), &averages);
    err |= clSetKernelArg(finalizeBandwidth, 1, sizeof(cl_mem),
        &(estimator->bandwidth_buffer));
    err |= clSetKernelArg(finalizeBandwidth, 2, sizeof(unsigned int), &i);
    err |= clSetKernelArg(finalizeBandwidth, 3, sizeof(unsigned int),
        &(estimator->nr_of_dimensions));
    err |= clSetKernelArg(finalizeBandwidth, 4, sizeof(unsigned int),
        &(estimator->rows_in_sample));
    Assert(err == CL_SUCCESS);

    err = clEnqueueNDRangeKernel(
        context->queue, finalizeBandwidth, 1, NULL, &dimensions, NULL,
        1, &variance_summation_event, &events[i]);
    Assert(err == CL_SUCCESS);
    // Clean up.
    err = clReleaseMemObject(buffers[i]);
    Assert(err == CL_SUCCESS);

    err |= clReleaseEvent(extraction_event);
    err |= clReleaseEvent(average_summation_event);
    err |= clReleaseEvent(variance_event);
    err |= clReleaseEvent(variance_summation_event);
    Assert(err == CL_SUCCESS);
  }
  err = clReleaseMemObject(averages);
  Assert(err == CL_SUCCESS);
  // Wait for the events to finalize:
  err = clWaitForEvents(estimator->nr_of_dimensions, events);
  Assert(err == CL_SUCCESS);
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    err = clReleaseEvent(events[i]);
    Assert(err == CL_SUCCESS);
  }
  // Clean up.
  free(variances);
  free(events);
}

void ocl_runModelOptimization(ocl_estimator_t* estimator) {
  if (estimator == NULL) return;
  cl_int err = CL_SUCCESS;
  // Set the rule-of-thumb bandwidth to initialize the estimator.
  ocl_setScottsBandwidth(estimator);
  // Now check if we do a full bandwidth optimization.
  if (!kde_enable_bandwidth_optimization) return;
  if (ocl_isDebug()) {
    fprintf(
        stderr, "Beginning model optimization for estimator on table %i\n",
        estimator->table);
  }
  // First, we need to fetch the feedback records for this table and push them
  // to the device.
  cl_mem device_ranges, device_selectivites;
  unsigned int feedback_records = ocl_prepareFeedback(
      estimator, &device_ranges, &device_selectivites);
  if (feedback_records == 0) return;

  // We need to transfer the bandwidth to the host.
  kde_float_t* fbandwidth = palloc(
      sizeof(kde_float_t) * estimator->nr_of_dimensions);
  ocl_context_t* context = ocl_getContext();
  err = clEnqueueReadBuffer(
      context->queue, estimator->bandwidth_buffer, CL_TRUE, 0,
      sizeof(kde_float_t) * estimator->nr_of_dimensions,
      fbandwidth, 0, NULL, NULL);
  estimator->stats->optimization_transfer_to_host++;
  Assert(err == CL_SUCCESS);
  // Cast to double (nlopt operates on double).
  double* bandwidth = lbfgs_malloc(estimator->nr_of_dimensions);
  unsigned int i;
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    bandwidth[i] = fbandwidth[i];
  }
  // Package all required buffers.
  optimization_config_t params;
  params.estimator = estimator;
  params.nr_of_observations = feedback_records;
  params.observed_ranges = device_ranges;
  params.observed_selectivities = device_selectivites;
  params.error_accumulator_buffer = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE,
      sizeof(kde_float_t) * feedback_records, NULL, &err);
  Assert(err == CL_SUCCESS);
  // Allocate a buffer to hold temporary gradient contributions. This buffer
  // will keep D contributions per observation. We store all contributions
  // consecutively (i.e. 111222333444). For optimal performance, we therefore
  // have to make sure that the consecutive regions (strides) have a size that
  // is aligned to the required machine alignment.
  params.stride_size = sizeof(kde_float_t) * feedback_records;
  if ((params.stride_size * 8) % context->required_mem_alignment) {
    // The stride size is misaligned, add some padding.
    params.stride_size *= 8;
    params.stride_size =
        (1 + params.stride_size / context->required_mem_alignment)
        * context->required_mem_alignment;
    params.stride_size /= 8;
  }
  params.gradient_accumulator_buffer = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE,
      estimator->nr_of_dimensions * params.stride_size,
      NULL, &err);
  Assert(err == CL_SUCCESS);
  params.gradient_buffer = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE,
      estimator->nr_of_dimensions * sizeof(kde_float_t), NULL, &err);
  Assert(err == CL_SUCCESS);
  params.error_buffer = clCreateBuffer(
      context->context, CL_MEM_READ_WRITE, sizeof(kde_float_t), NULL, &err);
  Assert(err == CL_SUCCESS);
  // Prepare the summation buffers.
  params.summation_descriptors = palloc(
      sizeof(ocl_aggregation_descriptor_t) * (1 + estimator->nr_of_dimensions));
  // Prepare the partial aggregations for the error.
  params.summation_descriptors[0] = prepareSumDescriptor(
      params.error_accumulator_buffer, params.nr_of_observations,
      params.error_buffer, 0);
  // .. and for each dimension.
  params.summation_buffers = palloc(
      sizeof(cl_mem) * estimator->nr_of_dimensions);
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    cl_buffer_region region;
    region.size = params.stride_size;
    region.origin = i * params.stride_size;
    params.summation_buffers[i] = clCreateSubBuffer(
        params.gradient_accumulator_buffer, CL_MEM_READ_ONLY,
        CL_BUFFER_CREATE_TYPE_REGION, &region, &err);
    Assert(err == CL_SUCCESS);
    params.summation_descriptors[i + 1] = prepareSumDescriptor(
        params.summation_buffers[i], params.nr_of_observations,
        params.gradient_buffer, i);
  }

  // Ok, we are prepared. Call the optimization routine.
  gettimeofday(&opt_start, NULL);
  evaluations = 0;
  fprintf(stderr, "> Starting numerical optimization of the model.\n");
  // Prepare the bound constraints.
  double* lower_bounds = palloc(sizeof(double) * estimator->nr_of_dimensions);
  double* upper_bounds = palloc(sizeof(double) * estimator->nr_of_dimensions);
  if(kde_bandwidth_representation == LOG_BW){
    for (i=0; i<estimator->nr_of_dimensions; ++i) {
      lower_bounds[i] = log(1e-20);    // We never want to be negative.
    }
    for (i=0; i<estimator->nr_of_dimensions; ++i) {
      upper_bounds[i] = log(4) + bandwidth[i];    // We never want to be negative.
    }
  }
  else {
    for (i=0; i<estimator->nr_of_dimensions; ++i) {
      lower_bounds[i] = 1e-20;    // We never want to be negative.
    }
    for (i=0; i<estimator->nr_of_dimensions; ++i) {
      upper_bounds[i] = 4 * bandwidth[i];
    }
  }
  double tmp;
  int nl_err;
  // We start with a global optimization step.
  nlopt_opt global_optimizer = nlopt_create(
      NLOPT_GD_MLSL, estimator->nr_of_dimensions);
  Assert(global_optimizer);
  nl_err = nlopt_set_lower_bounds(global_optimizer, lower_bounds);
  Assert(nl_err > 0 );
  nl_err = nlopt_set_upper_bounds(global_optimizer, upper_bounds);
  Assert(nl_err > 0 );
  nl_err = nlopt_set_maxeval(global_optimizer, 120);
  Assert(nl_err > 0 );
  nl_err = nlopt_set_min_objective(global_optimizer, computeGradient, &params);
  Assert(nl_err > 0 );
  // Register a local LBFGS instance for the global optimizer.
  nlopt_opt global_local_optimizer = nlopt_create(
      NLOPT_LD_LBFGS, estimator->nr_of_dimensions);
  nl_err = nlopt_set_maxeval(global_local_optimizer, 40);
  Assert(nl_err > 0 );
  nl_err = nlopt_set_local_optimizer(global_optimizer, global_local_optimizer);
  Assert(nl_err > 0 );
  fprintf(stderr, "> Running global pre-optimization: ");
  nl_err = nlopt_optimize(global_optimizer, bandwidth, &tmp);
  Assert(nl_err > 0 );
  fprintf(stderr, " done (%i, %f)\n", nl_err, tmp);
  // Prepare the local refinement.
  nlopt_opt local_optimizer = nlopt_create(
      NLOPT_LD_LBFGS, estimator->nr_of_dimensions);
  nlopt_set_lower_bounds(local_optimizer, lower_bounds);
  nlopt_set_min_objective(local_optimizer, computeGradient, &params);
  nlopt_set_ftol_abs(local_optimizer, 1e-20);
  nlopt_set_maxeval(local_optimizer, 100);
  fprintf(stderr, "> Running local refinement: ");
  err = nlopt_optimize(local_optimizer, bandwidth, &tmp);
  fprintf(stderr, " done (%i, %f)\n", err, tmp);
  if (ocl_isDebug()) {
    if (err < 0) {
      fprintf(stderr, "\nOptimization failed: %i!", err);
    } else {
      fprintf(stderr, "\nNew bandwidth:");
      for ( i = 0; i < estimator->nr_of_dimensions ; ++i)
        fprintf(stderr, " %e", bandwidth[i]);
      fprintf(stderr, "\n");
    }
  }
  nlopt_destroy(local_optimizer);
  // Transfer the bandwidth to the device.
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    fbandwidth[i] = bandwidth[i];
  }
  err = clEnqueueWriteBuffer(
      context->queue, estimator->bandwidth_buffer, CL_TRUE, 0,
      sizeof(kde_float_t) * estimator->nr_of_dimensions,
      fbandwidth, 0, NULL, NULL);
  estimator->stats->optimization_transfer_to_device++;
  Assert(err == CL_SUCCESS);
  // Clean up.
  pfree(fbandwidth);
  lbfgs_free(bandwidth);
  for (i=0; i<estimator->nr_of_dimensions; ++i) {
    err = clReleaseMemObject(params.summation_buffers[i]);
    Assert(err == CL_SUCCESS);
    releaseAggregationDescriptor(params.summation_descriptors[i + 1]);
  }
  releaseAggregationDescriptor(params.summation_descriptors[0]);
  pfree(params.summation_descriptors);
  pfree(params.summation_buffers);
  err |= clReleaseMemObject(params.error_buffer);
  err |= clReleaseMemObject(params.gradient_buffer);
  err |= clReleaseMemObject(params.error_accumulator_buffer);
  err |= clReleaseMemObject(params.gradient_accumulator_buffer);
  err |= clReleaseMemObject(device_ranges);
  err |= clReleaseMemObject(device_selectivites);
  Assert(err == CL_SUCCESS);
}


#pragma once

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "core/common.h"
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>

void batch_linterp_continuous_axis(float** yy, double* x, size_t size, size_t stride, size_t count);

void linterp_continuous_axis(float** yy, double* x, size_t size, double dt);

void gsl_interp_continuous_axis(float** yy, double* x, size_t size, double dx, gsl_interp_accel **acc, gsl_spline **spline);

void batch_gsl_interp_continuous_axis(float** yy, double* x, size_t size, size_t stride, size_t count);


/*
 * Copyright 2020 Makani Technologies LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SIM_MODELS_SENSORS_LOADCELL_H_
#define SIM_MODELS_SENSORS_LOADCELL_H_

#include <gsl/gsl_vector.h>
#include <stdint.h>
#include <functional>
#include <vector>

#include "common/c_math/vec2.h"
#include "common/c_math/vec3.h"
#include "common/macros.h"
#include "control/system_types.h"
#include "sim/faults/faults.h"
#include "sim/models/sensors/sensor.h"
#include "sim/models/signals/measurement.h"
#include "sim/sim_messages.h"

namespace sim {

void TetherForceToLoadcells(const WingParams &wing_params,
                            const LoadcellParams loadcell_params[],
                            const Vec3 &tether_force_b,
                            double loadcell_forces[]);

}  // namespace sim

class Loadcell : public Sensor {
  friend class LoadcellTest;

 public:
  Loadcell(const LoadcellParams *loadcell_params,
           const LoadcellSimParams &loadcell_sim_params,
           const WingParams &wing_params, FaultSchedule *faults);
  ~Loadcell() {}

  void UpdateSensorOutputs(SimSensorMessage *sensor_message,
                           TetherUpMessage * /*tether_up*/) const override;
  void Publish() const override;

  void set_Fb_tether(const Vec3 &val) { Fb_tether_.set_val(val); }
  void set_tether_released(bool val) { tether_released_.set_val(val); }

 private:
  void DiscreteStepHelper(double t) override;

  double tensions(int32_t i) const { return tensions_[i].recorded(); }
  const Vec3 &Fb_tether() const { return Fb_tether_.val(); }

  // Loadcell parameters.
  const LoadcellParams *loadcell_params_;
  const LoadcellSimParams &loadcell_sim_params_;
  const WingParams &wing_params_;

  // Input states.
  State<Vec3> Fb_tether_;
  State<bool> tether_released_;

  // Discrete state.
  std::vector<DiscreteState<double>> actual_tensions_;

  // Sub-models.
  std::vector<Measurement<double>> tensions_;

  DISALLOW_COPY_AND_ASSIGN(Loadcell);
};

#endif  // SIM_MODELS_SENSORS_LOADCELL_H_