#ifndef MSHADOW_TENSOR_BASE_H
#define MSHADOW_TENSOR_BASE_H
/*!
 * \file tensor_base.h
 * \brief definitions of base types, macros functions
 *
 * \author Bing Xu, Tianqi Chen
 */
#include <cmath>
#include <cstdio>
#include <cfloat>
#include <climits>
#include <algorithm>
// macro defintiions

/*!\brief if this macro is define to be 1, mshadow should compile without any of other libs */
#ifndef MSHADOW_STAND_ALONE
    #define MSHADOW_STAND_ALONE 0
#endif

/*! \brief whether do padding during allocation */
#ifndef MSHADOW_ALLOC_PAD
    #define MSHADOW_ALLOC_PAD true
#endif

/*!
 * \brief x dimension of data must be bigger pad_size * ratio to be alloced padded memory, otherwise use tide allocation
 *        for example, if pad_ratio=2, GPU memory alignement size is 32, then we will only allocate padded memory if x dimension > 64
 *        set it to 0 then we will always allocate padded memory
 */
#ifndef MSHADOW_MIN_PAD_RATIO
    #define MSHADOW_MIN_PAD_RATIO 2
#endif

#if MSHADOW_STAND_ALONE
   #define MSHADOW_USE_CBLAS 0
   #define MSHADOW_USE_MKL   0
   #define MSHADOW_USE_CUDA  0
#endif

/*! \brief use CBLAS for CBLAS */
#ifndef MSHADOW_USE_CBLAS
   #define MSHADOW_USE_CBLAS 0
#endif
/*! \brief use MKL for BLAS */
#ifndef MSHADOW_USE_MKL
   #define MSHADOW_USE_MKL   1
#endif
/*! \brief use CUDA support, must ensure that the cuda include path is correct, or directly compile using nvcc */
#ifndef MSHADOW_USE_CUDA
  #define MSHADOW_USE_CUDA   1
#endif
/*! \brief use single precition float */
#ifndef MSHADOW_SINGLE_PRECISION
  #define MSHADOW_SINGLE_PRECISION 1
#endif
/*! \brief whether use SSE */
#ifndef MSHADOW_USE_SSE
  #define MSHADOW_USE_SSE 1
#endif
/*! \brief whether use NVML to get dynamic info */
#ifndef MSHADOW_USE_NVML
  #define MSHADOW_USE_NVML 0
#endif
// SSE is conflict with cudacc
#ifdef __CUDACC__
  #undef MSHADOW_USE_SSE
  #define MSHADOW_USE_SSE 0
#endif

#if MSHADOW_USE_CBLAS
extern "C"{
    #include <cblas.h>
}
#elif MSHADOW_USE_MKL
  #include <mkl.h>
  #include <mkl_cblas.h>
  #include <mkl_vsl.h>
  #include <mkl_vsl_functions.h>
#endif

#if MSHADOW_USE_CUDA
  #include <cublas.h>
  #include <curand.h>
#endif

#if MSHADOW_USE_NVML
  #include <nvml.h>
#endif
// --------------------------------
// MSHADOW_XINLINE is used for inlining template code for both CUDA and CPU code.
#ifdef MSHADOW_XINLINE
  #error "MSHADOW_XINLINE must not be defined"
#endif
#ifdef __CUDACC__
  #define MSHADOW_XINLINE inline __attribute__((always_inline)) __device__ __host__
#else
  #define MSHADOW_XINLINE inline __attribute__((always_inline))
#endif
/*! \brief cpu force inline */
#define MSHADOW_CINLINE inline __attribute__((always_inline))

#if defined(__GXX_EXPERIMENTAL_CXX0X) || defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
  #define MSHADOW_CONSTEXPR constexpr
#else
  #define MSHADOW_CONSTEXPR const
#endif

/*! \brief namespace for mshadow */
namespace mshadow {
    /*! \brief buffer size for each random number generator */
    const unsigned kRandBufferSize = 1000000;
    /*! \brief pi  */
    const float kPi = 3.1415926f;

#if MSHADOW_SINGLE_PRECISION
    /*! \brief type that will be used for content */
    typedef float real_t;
#else
    typedef double real_t;
#endif
    /*! \brief type that will be used for index */
    typedef unsigned index_t;
}; // namespace mshadow

namespace mshadow {
    /*! \brief namespace for operators */
    namespace op {
        // binary operator
        /*! \brief mul operator */
        struct mul{
            /*! \brief map a, b to result using defined operation */
            MSHADOW_XINLINE static real_t Map(real_t a, real_t b) {
                return a * b;
            }
        };
        /*! \brief plus operator */
        struct plus {
            /*! \brief map a, b to result using defined operation */
            MSHADOW_XINLINE static real_t Map(real_t a, real_t b) {
                return a + b;
            }
        };
        /*! \brief minus operator */
        struct minus {
            /*! \brief map a, b to result using defined operation */
            MSHADOW_XINLINE static real_t Map(real_t a, real_t b) {
                return a - b;
            }
        };
        /*! \brief divide operator */
        struct div {
            /*! \brief map a, b to result using defined operation */
            MSHADOW_XINLINE static real_t Map(real_t a, real_t b) {
                return a / b;
            }
        };
        /*! \brief get rhs */
        struct right {
            /*! \brief map a, b to result using defined operation */
            MSHADOW_XINLINE static real_t Map(real_t a, real_t b) {
                return b;
            }
        };
    }; // namespace op

    /*! \brief namespace for savers */
    namespace sv {
        /*! \brief save to saver: = */
        struct saveto {
            /*! \brief save b to a using save method */
            MSHADOW_XINLINE static void Save(real_t& a, real_t b) {
                a  = b;
            }
            /*! \brief helper constant to use BLAS, alpha */
            MSHADOW_CONSTEXPR static real_t kAlphaBLAS = 1.0f;
            /*! \brief helper constant to use BLAS, beta */
            MSHADOW_CONSTEXPR static real_t kBetaBLAS  = 0.0f;
            /*! \brief corresponding binary operator type */
            typedef op::right OPType;
        };
        /*! \brief save to saver: += */
        struct plusto {
            /*! \brief save b to a using save method */
            MSHADOW_XINLINE static void Save(real_t& a, real_t b) {
                a += b;
            }
            /*! \brief helper constant to use BLAS, alpha */
            MSHADOW_CONSTEXPR static real_t kAlphaBLAS = 1.0f;
            /*! \brief helper constant to use BLAS, beta */
            MSHADOW_CONSTEXPR static real_t kBetaBLAS  = 1.0f;
            /*! \brief corresponding binary operator type */
            typedef op::plus OPType;
        };
        /*! \brief minus to saver: -= */
        struct minusto {
            /*! \brief save b to a using save method */
            MSHADOW_XINLINE static void Save(real_t& a, real_t b) {
                a -= b;
            }
            /*! \brief helper constant to use BLAS, alpha */
            MSHADOW_CONSTEXPR static real_t kAlphaBLAS = -1.0f;
            /*! \brief helper constant to use BLAS, beta */
            MSHADOW_CONSTEXPR static real_t kBetaBLAS  = 1.0f;
            /*! \brief corresponding binary operator type */
            typedef op::minus OPType;
        };
        /*! \brief multiply to saver: *= */
        struct multo {
            /*! \brief save b to a using save method */
            MSHADOW_XINLINE static void Save(real_t& a, real_t b) {
                a *= b;
            }
            /*! \brief corresponding binary operator type */
            typedef op::mul OPType;
        };
        /*! \brief divide to saver: /= */
        struct divto {
            /*! \brief save b to a using save method */
            MSHADOW_XINLINE static void Save(real_t& a, real_t b) {
                a /= b;
            }
            /*! \brief corresponding binary operator type */
            typedef op::div OPType;
        };
    }; // namespace sv

    namespace op {
        // unary operator/ function: example
        // these operators can be defined by user, in the same style as binary and unary operator
        // to use, simply write F<op::identity>( src )
        /*! \brief identity function that maps a real number to it self */
        struct identity{
            /*! \brief map a to result using defined operation */
            MSHADOW_XINLINE static real_t Map(real_t a) {
                return a;
            }
        };
    }; // namespace op

    /*! \brief namespace for potential reducer operations */
    namespace red {
        /*! \brief sum reducer */
        struct sum {
            /*! \brief do reduction into dst */
            MSHADOW_XINLINE static void Reduce( volatile real_t& dst,  volatile real_t src ) {
                dst += src;
            }
            /*! \brief calculate gradient of redres with respect to redsrc,  redres: reduced result, redsrc: one of reduction element */
            MSHADOW_XINLINE static real_t PartialGrad( real_t redres, real_t redsrc ) {
                return 1.0f;
            }
            /*! \brief an intial value of reducer */
            MSHADOW_CONSTEXPR static real_t kInitV = 0.0f;
        };
        /*! \brief maximum reducer */
        struct maximum {
            /*! \brief do reduction into dst */
            MSHADOW_XINLINE static void Reduce( volatile real_t& dst,  volatile real_t src ) {
                using namespace std;
                dst = max( dst, src );
            }
            /*! \brief calculate gradient of redres with respect to redsrc,  redres: reduced result, redsrc: one of reduction element */
            MSHADOW_XINLINE static real_t PartialGrad( real_t redres, real_t redsrc ) {
                return redres == redsrc ? 1.0f: 0.0f;
            }
            /*! \brief an intial value of reducer */
#if MSHADOW_SINGLE_PRECISION
            MSHADOW_CONSTEXPR static real_t kInitV = -FLT_MAX;
#else
            MSHADOW_CONSTEXPR static real_t kInitV = -DBL_MAX;
#endif
        };
    };

    /*! \brief namespace for helper utils of the project */
    namespace utils{
        /*! \brief send error message then exit */
        inline void Error( const char *msg ){
            fprintf( stderr, "Error:%s\n",msg );
            exit( -1 );
        }
        /*! \brief assert a expression is true */
        inline void Assert( bool exp ){
            if( !exp ) Error( "AssertError" );
        }
        /*! \brief assert a expression is true */
        inline void Assert( bool exp, const char *msg ){
            if( !exp ) Error( msg );
        }
        /*! \brief warning */
        inline void Warning( const char *msg ){
            fprintf( stderr, "warning:%s\n",msg );
        }
    }; // namespace utils
}; // namespace mshadow
#endif // TENSOR_BASE_H


#ifndef IMAGE_H_YQCE0AWR
#define IMAGE_H_YQCE0AWR

#include <gsl/gsl>
#include <opencv2/core/mat.hpp>
#include <sens_loc/math/coordinate.h>
#include <type_traits>

namespace sens_loc::math {

namespace detail {
template <typename Number>  // requires Number<Number>
inline int get_opencv_type() {
    static_assert(std::is_arithmetic_v<Number>);

    if constexpr (std::is_same<Number, float>::value)
        return CV_32F;  // NOLINT(bugprone-branch-clone)
    else if constexpr (std::is_same<Number, double>::value)
        return CV_64F;
    else if constexpr (std::is_same<Number, uchar>::value)
        return CV_8U;
    else if constexpr (std::is_same<Number, schar>::value)
        return CV_8S;
    else if constexpr (std::is_same<Number, ushort>::value)
        return CV_16U;
    else if constexpr (std::is_same<Number, short>::value)
        return CV_16S;
    else
        return -1;
}
}  // namespace detail

/// This function encapsulates an \c cv::Mat and ensures the common
/// preconditions for this code base are enforced.
///
/// \invariant the image has only 1 channel.
/// \invariant the underlying data types are consistent
/// \invariant access is only done in \p pixel_coord<int>
///
/// \tparam PixelType the underlying type of the \c cv::Mat
/// \sa math::pixel_coord
template <typename PixelType = ushort>
class image {
  public:
    static_assert(std::is_arithmetic_v<PixelType>);

    image() = default;

    /// Construct the image from a \c cv::Mat and uphold the class invariant.
    explicit image(const cv::Mat& image) noexcept
        : _data(image) {
        Expects(image.type() == detail::get_opencv_type<PixelType>());
        Expects(image.channels() == 1);
        Expects(!image.empty());
    }
    image(const image<PixelType>& other) = default;

    // NOLINTNEXTLINE(performance-noexcept-move-constructor)
    image(image<PixelType>&& other) = default;
    explicit image(cv::Mat&& other) noexcept
        : _data(std::move(other)) {
        Expects(_data.type() == detail::get_opencv_type<PixelType>());
        Expects(_data.channels() == 1);
        Expects(!_data.empty());
    }

    image<PixelType>& operator=(const image<PixelType>& other) = default;
    image<PixelType>& operator=(const cv::Mat& other) noexcept {
        Expects(other.type() == detail::get_opencv_type<PixelType>());
        Expects(other.channels() == 1);
        Expects(!other.empty());
        _data = other;
        return *this;
    }

    // NOLINTNEXTLINE(performance-noexcept-move-constructor)
    image<PixelType>& operator=(image<PixelType>&& other) = default;
    image<PixelType>& operator=(cv::Mat&& other) noexcept {
        Expects(other.type() == detail::get_opencv_type<PixelType>());
        Expects(other.channels() == 1);
        Expects(!other.empty());
        _data = std::move(other);
        return *this;
    }

    ~image() = default;

    /// Return the width of the image.
    [[nodiscard]] int w() const noexcept { return _data.cols; }
    /// Return the height of the image.
    [[nodiscard]] int h() const noexcept { return _data.rows; }

    /// Read-Access in the image for some pixel \p p.
    template <typename Number = int>
    [[nodiscard]] PixelType at(const pixel_coord<Number>& p) const noexcept {
        static_assert(std::is_arithmetic_v<Number>);

        return _data.at<PixelType>(gsl::narrow_cast<int>(p.v()),
                                   gsl::narrow_cast<int>(p.u()));
    }
    /// Write-Access in the image for some pixel \p p.
    template <typename Number = int>
    [[nodiscard]] PixelType& at(const pixel_coord<Number>& p) noexcept {
        static_assert(std::is_arithmetic_v<Number>);

        return _data.at<PixelType>(gsl::narrow_cast<int>(p.v()),
                                   gsl::narrow_cast<int>(p.u()));
    }

    /// Get access to the underlying data to use it for normal cv operations.
    [[nodiscard]] const cv::Mat& data() const noexcept { return _data; }

  private:
    cv::Mat _data;
};

template <typename TargetType, typename PixelType>
image<TargetType> convert(const image<PixelType>& img) noexcept {
    static_assert(std::is_arithmetic_v<TargetType>);
    static_assert(std::is_arithmetic_v<PixelType>);

    if constexpr (std::is_same_v<TargetType, PixelType>)
        return img; // NOLINT(bugprone-suspicious-semicolon)

    cv::Mat tmp(img.h(), img.w(), detail::get_opencv_type<TargetType>());
    img.data().convertTo(tmp, detail::get_opencv_type<TargetType>());
    return math::image<TargetType>(std::move(tmp));
}

}  // namespace sens_loc::math

#endif /* end of include guard: IMAGE_H_YQCE0AWR */


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

#ifndef _MODEL1_H
#define _MODEL1_H

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

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

extern int which_level_update;
extern double *limits;     // limits from dnest
extern int thistask, totaltask;

extern DNestFptrSet *fptrset_thismodel;

/* functions */
void from_prior_thismodel(void *model);
void print_particle_thismodel(FILE *fp, const void *model);
double log_likelihoods_cal_thismodel(const void *model);
double perturb_thismodel(void *model);
void restart_action_model1(int iflag);
#endif


/*
* -----------------------------------------------------------------
*  Utilities Library --- util_lib.c
*  Version: 1.6180
*  Date: Mar 12, 2010
* -----------------------------------------------------------------
*  Programmer: Americo Barbosa da Cunha Junior
*              americo.cunhajr@gmail.com
* -----------------------------------------------------------------
*  Copyright (c) 2010 by Americo Barbosa da Cunha Junior
*
*  This program is free software: you can redistribute it and/or
*  modify it under the terms of the GNU General Public License as
*  published by the Free Software Foundation, either version 3 of
*  the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  A copy of the GNU General Public License is available in
*  LICENSE.txt or http://www.gnu.org/licenses/.
* -----------------------------------------------------------------
*  This is the implementation file of a library
*  with miscelaneous functions.
* -----------------------------------------------------------------
*/

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

#include "../include/util_lib.h"

/*
*------------------------------------------------------------
*   gsl_fprint_vector
*
*   This function prints a vector in a file.
*
*   Input:
*   file - output file
*   v    - GSL vector
*
*   Output:
*
*   Return:
*   success or error
*
*   last update: Nov 6, 2008
*------------------------------------------------------------
*/

int gsl_fprint_vector(FILE* file,gsl_vector *v)
{
    if( v == NULL )
	return GSL_EINVAL;

    unsigned int i;

    for( i = 0; i < v->size; i++ )
	fprintf(file," %+.3e", v->data[i]);

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_print_vector
*
*   This function prints a vector on the screen
*
*   Input:
*   v  - GSL vector
*
*   Output:
*
*   Return:
*   success or error
*
*   last update: Dec 26, 2008
*------------------------------------------------------------
*/

int gsl_print_vector(gsl_vector *v)
{
    if( v == NULL )
	return GSL_EINVAL;

    unsigned int i;

    for( i = 0; i < v->size; i++ )
	printf(" %+.3e", v->data[i]);

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_print_matrix_line
*
*   This function prints a matrix line on the screen.
*
*   Input:
*   i  - matrix line to be printed
*   M  - GSL matrix
*
*   Output:
*
*   Return:
*   success or error
*
*   last update: Dec 26, 2008
*------------------------------------------------------------
*/

int gsl_print_matrix_line(unsigned int i,gsl_matrix *M)
{
    if( M == NULL )
	return GSL_EINVAL;

    unsigned int j;

    printf("\n");
    for( j = 0; j < M->size2; j++ )
	printf(" %+.3e ", M->data[i*M->tda+j]);

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_print_matrix_column
*
*   This function prints a matrix column on the screen
*
*   Input:
*   M  - GSL matrix
*   i  - matrix column to be printed
*
*   Output:
*
*   Return:
*   success or error
*
*   last update: Dec 26, 2008
*------------------------------------------------------------
*/

int gsl_print_matrix_column(unsigned int j,gsl_matrix *M)
{
    if( M == NULL )
    	return GSL_EINVAL;

    unsigned int i;

    printf("\n");
    for( i = 0; i < M->size1; i++ )
	printf(" %+.3e\n", M->data[i*M->tda+j]);

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_print_matrix
*
*   This function prints a matrix on the screen
*
*   Input:
*   M  - GSL matrix
*
*   Outpur:
*
*   Return:
*   success or error
*
*   last update: Dec 26, 2008
*------------------------------------------------------------
*/

int gsl_print_matrix(gsl_matrix *M)
{
    if( M == NULL )
    	return GSL_EINVAL;

    unsigned int i;

    for( i = 0; i < M->size1; i++ )
	gsl_print_matrix_line(i,M);

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_rand_vector
*
*   This function creates a random vector.
*
*   Input:
*   seed - random number generator seed
*
*   Output:
*   v  - GSL vector
*
*   Return:
*   success or error
*
*   last update: Feb 19, 2010
*------------------------------------------------------------
*/

int gsl_rand_vector(gsl_rng *r,gsl_vector *v)
{
    if( v == NULL )
    	return GSL_EINVAL;

    unsigned int i;

    for( i = 0; i < v->size; i++ )
	v->data[i] = gsl_rng_uniform(r) + 1.0;

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_rand_matrix
*
*   This function creates a radom matrix.
*
*   Input:
*   seed - random number generator seed
*
*   Output:
*   M    - GSL matrix
*
*   Return:
*   success or error
*
*   last update: Feb 27, 2009
*------------------------------------------------------------
*/

int gsl_rand_matrix(gsl_rng *r,gsl_matrix *M)
{
    if( M == NULL )
    	return GSL_EINVAL;

    unsigned int i, j;

    for( i = 0; i < M->size1; i++ )
        for( j = 0; j < M->size2; j++ )
            M->data[i*M->tda+j] = gsl_rng_uniform(r) + 1.0;

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   gsl_diagonal_matrix
*
*   This function creates a diagonal matrix with the elements
*   of the vector v.
*
*   Input:
*   v - GSL vector
*
*   Output:
*   D  - GSL matrix
*
*   Return:
*   success or error
*
*   last update: Feb 26, 2009
*------------------------------------------------------------
*/

int gsl_diagonal_matrix(gsl_vector *v,gsl_matrix *D)
{
    if( v == NULL || D == NULL )
    	return GSL_EINVAL;

    unsigned int i, j;

    for( i = 0; i < D->size1; i++ )
        for( j = 0; j < D->size2; j++ )
            if( i == j )
                D->data[i*D->tda+j] = v->data[i];
            else
                D->data[i*D->tda+j] = 0.0;

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   util_time_used
*
*   This function computes the CPU time and Wall time
*   used in a simulation.
*
*   Input:
*   cpu_start  - cpu clock start flag
*   cpu_end    - cpu clock end flag
*   wall_start - wall clock start flag
*   wall_end   - wall clock end flag
*
*   last update: Nov 10, 2009
*------------------------------------------------------------
*/

void util_time_used(clock_t cpu_start,clock_t cpu_end,
			    time_t wall_start,time_t wall_end)
{
    double cpu_used;
    double wall_used;

    cpu_used  = ((double) (cpu_end - cpu_start)) / CLOCKS_PER_SEC;
    wall_used = difftime(wall_end,wall_start);

    printf("\n");
    printf("\n CPU  time (s): %+.6e",cpu_used);
    printf("\n Wall time (s): %+.6e",wall_used);
    printf("\n");

    return;
}
/*------------------------------------------------------------*/


/* Starting from version 7.8, MATLAB BLAS expects ptrdiff_t arguments for integers */
#if MATLAB_VERSION >= 0x0708
#include <stddef.h>
#include <stdlib.h>
#endif
#include <string.h>

/* Define MX_HAS_INTERLEAVED_COMPLEX for version <9.4 */
#ifndef MX_HAS_INTERLEAVED_COMPLEX
#define MX_HAS_INTERLEAVED_COMPLEX 0
#endif

/* Starting from version 7.6, MATLAB BLAS is seperated */
#if MATLAB_VERSION >= 0x0705
#include <blas.h>
#endif
#include <lapack.h>
#include "f2c.h"

// Conversion of optimal problems with coupling weighting terms to standard problems
#define sb02mt FORTRAN_WRAPPER(sb02mt)
extern void sb02mt(
        const char *jobg,
        const char *jobl,
        const char *fact,
        const char *uplo,
        const ptrdiff_t *n,
        const ptrdiff_t *m,
        double *a,
        const ptrdiff_t *lda,
        double *b,
        const ptrdiff_t *ldb,
        double *q,
        const ptrdiff_t *ldq,
        double *r,
        const ptrdiff_t *ldr,
        double *l,
        const ptrdiff_t *ldl,
        ptrdiff_t *ipiv,
        ptrdiff_t *oufact,
        double *g,
        const ptrdiff_t *ldg,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Conversion of optimal problems with coupling weighting terms to standard problems (more flexibility)
#define sb02mx FORTRAN_WRAPPER(sb02mx)
extern void sb02mx(
        const char *jobg,
        const char *jobl,
        const char *fact,
        const char *uplo,
        const char *trans,
        const char *flag,
        const char *def,
        const ptrdiff_t *n,
        const ptrdiff_t *m,
        double *a,
        const ptrdiff_t *lda,
        double *b,
        const ptrdiff_t *ldb,
        double *q,
        const ptrdiff_t *ldq,
        double *r,
        const ptrdiff_t *ldr,
        double *l,
        const ptrdiff_t *ldl,
        ptrdiff_t *ipiv,
        ptrdiff_t *oufact,
        double *g,
        const ptrdiff_t *ldg,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Constructing the 2n-by-2n Hamiltonian or symplectic matrix for linear-quadratic optimization problems
#define sb02mu FORTRAN_WRAPPER(sb02mu)
extern void sb02mu(
        const char *dico,
        const char *hinv,
        const char *uplo,
        const ptrdiff_t *n,
        double *a,
        const ptrdiff_t *lda,
        const double *g,
        const ptrdiff_t *ldg,
        const double *q,
        const ptrdiff_t *ldq,
        double *s,
        const ptrdiff_t *lds,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Constructing the 2n-by-2n Hamiltonian or symplectic matrix for linear-quadratic optimization problems (improved)
#define sb02ru FORTRAN_WRAPPER(sb02ru)
extern void sb02ru(
        const char *dico,
        const char *hinv,
        const char *trana,
        const char *uplo,
        const ptrdiff_t *n,
        const double *a,
        const ptrdiff_t *lda,
        double *g,
        const ptrdiff_t *ldg,
        double *q,
        const ptrdiff_t *ldq,
        double *s,
        const ptrdiff_t *lds,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Constructing the extended Hamiltonian or symplectic matrix pairs for linear-quadratic optimization problems, and compressing them to 2N-by-2N matrices
#define sb02oy FORTRAN_WRAPPER(sb02oy)
extern void sb02oy(
        const char *type,
        const char *dico,
        const char *jobb,
        const char *fact,
        const char *uplo,
        const char *jobl,
        const char *jobe,
        const ptrdiff_t *n,
        const ptrdiff_t *m,
        const ptrdiff_t *p,
        const double *a,
        const ptrdiff_t *lda,
        const double *b,
        const ptrdiff_t *ldb,
        const double *q,
        const ptrdiff_t *ldq,
        const double *r,
        const ptrdiff_t *ldr,
        const double *l,
        const ptrdiff_t *ldl,
        const double *e,
        const ptrdiff_t *lde,
        double *af,
        const ptrdiff_t *ldaf,
        double *bf,
        const ptrdiff_t *ldbf,
        double *tol,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Optimal state feedback matrix for an optimal control problem
#define sb02nd FORTRAN_WRAPPER(sb02nd)
extern void sb02nd(
        const char *dico,
        const char *fact,
        const char *uplo,
        const char *jobl,
        const ptrdiff_t *n,
        const ptrdiff_t *m,
        const ptrdiff_t *p,
        const double *a,
        const ptrdiff_t *lda,
        double *b,
        const ptrdiff_t *ldb,
        double *r,
        const ptrdiff_t *ldr,
        ptrdiff_t *ipiv,
        const double *l,
        const ptrdiff_t *ldl,
        double *x,
        const ptrdiff_t *ldx,
        const double *rnorm,
        double *f,
        const ptrdiff_t *ldf,
        ptrdiff_t *oufact,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Solution of continuous- or discrete-time algebraic Riccati equations for descriptor systems
#define sg02nd FORTRAN_WRAPPER(sg02nd)
extern void sg02nd(
        const char *dico,
        const char *jobe,
        const char *job,
        const char *jobx,
        const char *fact,
        const char *uplo,
        const char *jobl,
        const char *trans,
        const ptrdiff_t *n,
        const ptrdiff_t *m,
        const ptrdiff_t *p,
        const double *a,
        const ptrdiff_t *lda,
        const double *e,
        const ptrdiff_t *lde,
        double *b,
        const ptrdiff_t *ldb,
        double *r,
        const ptrdiff_t *ldr,
        ptrdiff_t *ipiv,
        const double *l,
        const ptrdiff_t *ldl,
        double *x,
        const ptrdiff_t *ldx,
        const double *rnorm,
        double *k,
        const ptrdiff_t *ldk,
        double *h,
        const ptrdiff_t *ldh,
        double *xe,
        const ptrdiff_t *ldxe,
        ptrdiff_t *oufact,
        ptrdiff_t *iwork,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Column interchanges in a complex matrix
#define ma02gz FORTRAN_WRAPPER(ma02gz)
extern void ma02gz(
        const ptrdiff_t *n,
        double *a,
        const ptrdiff_t *lda,
        const ptrdiff_t *k1,
        const ptrdiff_t *k2,
        const ptrdiff_t *ipiv,
        const ptrdiff_t *incx
        );

// Solution of linear equations X op(A) = B
#define mb02vd FORTRAN_WRAPPER(mb02vd)
extern void mb02vd(
        const char *trans,
        const ptrdiff_t *m,
        const ptrdiff_t *n,
        double *a,
        const ptrdiff_t *lda,
        ptrdiff_t *ipiv,
        double *b,
        const ptrdiff_t *ldb,
        ptrdiff_t *info
        );

// Periodic Hessenberg form of a product of p matrices using orthogonal similarity transformations
#define mb03vd FORTRAN_WRAPPER(mb03vd)
extern void mb03vd(
        const ptrdiff_t *n,
        const ptrdiff_t *p,
        const ptrdiff_t *ilo,
        const ptrdiff_t *ihi,
        double *a,
        const ptrdiff_t *lda1,
        const ptrdiff_t *lda2,
        double *tau,
        const ptrdiff_t *ldtau,
        double *dwork,
        ptrdiff_t *info
        );

// Orthogonal matrices for reduction to periodic Hessenberg form of a product of matrices
#define mb03vy FORTRAN_WRAPPER(mb03vy)
extern void mb03vy(
        const ptrdiff_t *n,
        const ptrdiff_t *p,
        const ptrdiff_t *ilo,
        const ptrdiff_t *ihi,
        double *a,
        const ptrdiff_t *lda1,
        const ptrdiff_t *lda2,
        double *tau,
        const ptrdiff_t *ldtau,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Schur decomposition and eigenvalues of a product of matrices in periodic Hessenberg form
#define mb03wd FORTRAN_WRAPPER(mb03wd)
extern void mb03wd(
        const char *job,
        const char *compz,
        const ptrdiff_t *n,
        const ptrdiff_t *p,
        const ptrdiff_t *ilo,
        const ptrdiff_t *ihi,
        const ptrdiff_t *iloz,
        const ptrdiff_t *ihiz,
        double *h,
        const ptrdiff_t *ldh1,
        const ptrdiff_t *ldh2,
        double *z,
        const ptrdiff_t *ldz1,
        const ptrdiff_t *ldz2,
        double *wr,
        double *wi,
        double *dwork,
        const ptrdiff_t *ldwork,
        ptrdiff_t *info
        );

// Computes the general product of K complex scalars trying to avoid over- and underflow.
#define zlapr1 FORTRAN_WRAPPER(zlapr1)
extern int zlapr1(
        doublereal *base,
        integer *k,
        integer *s,
        doublecomplex *a,
        integer *inca,
        doublecomplex *alpha,
        doublecomplex *beta,
        integer *scal
        );

// Finding the eigenvalues of the complex generalized matrix product.
#define zpgeqz FORTRAN_WRAPPER(zpgeqz)
extern int zpgeqz(
        char *job,
        char *compq,
        integer *k,
        integer *n,
        integer *ilo,
        integer *ihi,
        integer *s,
        doublecomplex *a,
        integer *lda1,
        integer *lda2,
        doublecomplex *alpha,
        doublecomplex *beta,
        integer *scal,
        doublecomplex *q,
        integer *ldq1,
        integer *ldq2,
        doublereal *dwork,
        integer *ldwork,
        doublecomplex *zwork,
        integer *lzwork,
        integer *info
        );

// Swaps adjacent diagonal 1-by-1 blocks in a complex generalized matrix product.
#define zpgex2 FORTRAN_WRAPPER(zpgex2)
extern int zpgex2(
        logical *wantq,
        integer *k,
        integer *n,
        integer *j,
        integer *s,
        doublecomplex *a,
        integer *lda1,
        integer *lda2,
        doublecomplex *q,
        integer *ldq1,
        integer *ldq2,
        doublecomplex *zwork,
        integer *info
        );

// Swaps adjacent diagonal 1-by-1 blocks in a complex generalized matrix product.
#define zpghrd FORTRAN_WRAPPER(zpghrd)
extern int zpghrd(
        char *compq,
        integer *k,
        integer *n,
        integer *ilo,
        integer *ihi,
        integer *s,
        doublecomplex *a,
        integer *lda1,
        integer *lda2,
        doublecomplex *q,
        integer *ldq1,
        integer *ldq2,
        doublereal *dwork,
        integer *ldwork,
        doublecomplex *zwork,
        integer *lzwork,
        integer *info
        );

// Reorders the periodic Schur decomposition of a complex generalized matrix product.
#define zpgord FORTRAN_WRAPPER(zpgord)
extern int zpgord(
        logical *wantq,
        integer *k,
        integer *n,
        integer *s,
        logical *select,
        doublecomplex *a,
        integer *lda1,
        integer *lda2,
        doublecomplex *alpha,
        doublecomplex *beta,
        integer *scal,
        doublecomplex *q,
        integer *ldq1,
        integer *ldq2,
        integer *m,
        doublecomplex *zwork,
        integer *lzwork,
        integer *info
        );


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

/***************************************************************************//**
 *
 **/
void QUARK_CORE_clauum(Quark *quark, Quark_Task_Flags *task_flags,
                       PLASMA_enum uplo, int n, int nb,
                       PLASMA_Complex32_t *A, int lda)
{
    DAG_CORE_LAUUM;
    QUARK_Insert_Task(quark, CORE_clauum_quark, task_flags,
        sizeof(PLASMA_enum),                &uplo,  VALUE,
        sizeof(int),                        &n,     VALUE,
        sizeof(PLASMA_Complex32_t)*nb*nb,    A,             INOUT,
        sizeof(int),                        &lda,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_clauum_quark = PCORE_clauum_quark
#define CORE_clauum_quark PCORE_clauum_quark
#endif
void CORE_clauum_quark(Quark *quark)
{
    PLASMA_enum uplo;
    int N;
    PLASMA_Complex32_t *A;
    int LDA;

    quark_unpack_args_4(quark, uplo, N, A, LDA);
    LAPACKE_clauum_work(LAPACK_COL_MAJOR, lapack_const(uplo), N, A, LDA);
}


//
// C++ Interface: %{MODULE}
//
// Description:
//
//
// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
//
// Copyright: See COPYING file that comes with this distribution
//
//

#ifndef TNet_Vector_h
#define TNet_Vector_h

#include <cstddef>
#include <cstdlib>
#include <stdexcept>
#include <iostream>

#ifdef HAVE_ATLAS
extern "C"{
  #include <cblas.h>
  #include <clapack.h>
}
#endif

#include "Common.h"
#include "MathAux.h"
#include "Types.h"
#include "Error.h"

namespace TNet
{
  template<typename _ElemT> class Vector;
  template<typename _ElemT> class SubVector;
  template<typename _ElemT> class Matrix;
  template<typename _ElemT> class SpMatrix;

  // we need to declare the friend functions here
  template<typename _ElemT>
    std::ostream & operator << (std::ostream & rOut, const Vector<_ElemT> & rV);

  template<typename _ElemT>
    std::istream & operator >> (std::istream & rIn, Vector<_ElemT> & rV);

  template<typename _ElemT>
    _ElemT
    BlasDot(const Vector<_ElemT>& rA, const Vector<_ElemT>& rB);

  /** **************************************************************************
   ** **************************************************************************
   *  @brief Provides a matrix abstraction class
   *
   *  This class provides a way to work with matrices in TNet.
   *  It encapsulates basic operations and memory optimizations.
   *
   */
  template<typename _ElemT>
    class Vector
    {
    public:

    /// defines a type of this
    typedef Vector<_ElemT> ThisType;

    Vector(): mpData(NULL)
#ifdef STK_MEMALIGN_MANUAL
    ,mpFreeData(NULL)
#endif
    , mDim(0)
      {}

      /**
       * @brief Copy constructor
       * @param rV
       */
      Vector(const Vector<_ElemT>& rV)
  	  { mpData=NULL; Init(rV.Dim()); Copy(rV); }

      /* Type conversion constructor. */
      template<typename _ElemU>
      explicit Vector(const Vector<_ElemU>& rV)
  	  { mpData=NULL; Init(rV.Dim()); Copy(rV); }

      Vector(const _ElemT* ppData, const size_t s)
      { mpData=NULL; Init(s); Copy(ppData); }

      explicit Vector(const size_t s, bool clear=true)
      { mpData=NULL; Init(s,clear); }

      ~Vector()
      { Destroy(); }

       Vector<_ElemT> &operator = (const Vector <_ElemT> &other)
       { Init(other.Dim()); Copy(other); return *this; } // Needed for inclusion in std::vector

      Vector<_ElemT>&
      Init(size_t length, bool clear=true);

      /**
       * @brief Dealocates the window from memory and resets the dimensions to (0)
       */
      void
      Destroy();

      /**
       * @brief Returns @c true if vector is initialized
       */
      bool
      IsInitialized() const
      { return mpData != NULL; }

      /**
       * @brief Sets all elements to 0
       */
      void
      Zero();

      void
      Set(_ElemT f);

      inline size_t
      Dim() const
      { return mDim; }

      /**
       * @brief Returns size of matrix in memory (in bytes)
       */
      inline size_t
      MSize() const
      {
        return (mDim + (((16 / sizeof(_ElemT)) - mDim%(16 / sizeof(_ElemT)))
                          % (16 / sizeof(_ElemT)))) * sizeof(_ElemT);
      }

      /**
       *  @brief Gives access to the vector memory area
       *  @return pointer to the first field
       */
      inline _ElemT*
      pData()
      { return mpData; }

      /**
       *  @brief Gives access to the vector memory area
       *  @return pointer to the first field (const version)
       */
      inline const _ElemT*
      pData() const
      { return mpData; }

      /**
       *  @brief Gives access to a specified vector element (const).
       */
      inline _ElemT
      operator [] (size_t i) const
      {
#ifdef PARANOID
		assert(i<mDim);
#endif
		return *(mpData + i);
	  }

      /**
       *  @brief Gives access to a specified vector element (non-const).
       */
      inline _ElemT &
      operator [] (size_t i)
      {
#ifdef PARANOID
		assert(i<mDim);
#endif
		return *(mpData + i);
	  }

      /**
       *  @brief Gives access to a specified vector element (const).
       */
      inline _ElemT
		operator () (size_t i) const
      {
#ifdef PARANOID
		assert(i<mDim);
#endif
		return *(mpData + i);
	  }

      /**
       *  @brief Gives access to a specified vector element (non-const).
       */
      inline _ElemT &
		operator () (size_t i)
      {
#ifdef PARANOID
		assert(i<mDim);
#endif
		return *(mpData + i);
	  }

      /**
       * @brief Returns a matrix sub-range
       * @param o Origin
       * @param l Length
       * See @c SubVector class for details
       */
      SubVector<_ElemT>
      Range(const size_t o, const size_t l)
      { return SubVector<_ElemT>(*this, o, l); }

      /**
       * @brief Returns a matrix sub-range
       * @param o Origin
       * @param l Length
       * See @c SubVector class for details
       */
      const SubVector<_ElemT>
      Range(const size_t o, const size_t l) const
      { return SubVector<_ElemT>(*this, o, l); }

      //########################################################################
      //########################################################################

      /// Copy data from another vector
      Vector<_ElemT>&
      Copy(const Vector<_ElemT>& rV);

      /// Copy data from another vector of a different type.
      template<typename _ElemU> Vector<_ElemT>&
      Copy(const Vector<_ElemU>& rV);

      /// Load data into the vector
      Vector<_ElemT>&
      Copy(const _ElemT* ppData);

      Vector<_ElemT>&
      CopyVectorizedMatrixRows(const Matrix<_ElemT> &rM);

      Vector<_ElemT>&
      RemoveElement(size_t i);

      Vector<_ElemT>&
      ApplyLog();

      Vector<_ElemT>&
      ApplyLog(const Vector<_ElemT>& rV);//ApplyLog to rV and put the result in (*this)

      Vector<_ElemT>&
      ApplyExp();

      Vector<_ElemT>&
      ApplySoftMax();

      Vector<_ElemT>&
      Invert();

      Vector<_ElemT>&
      DotMul(const Vector<_ElemT>& rV); // Multiplies each element (*this)(i) by rV(i).

      Vector<_ElemT>&
      BlasAxpy(const _ElemT alpha, const Vector<_ElemT>& rV);

      Vector<_ElemT>&
      BlasGemv(const _ElemT alpha, const Matrix<_ElemT>& rM, const MatrixTrasposeType trans, const Vector<_ElemT>& rV, const _ElemT beta = 0.0);

      //########################################################################
      //########################################################################

      Vector<_ElemT>&
      Add(const Vector<_ElemT>& rV)
      { return BlasAxpy(1.0, rV); }

      Vector<_ElemT>&
      Subtract(const Vector<_ElemT>& rV)
      { return BlasAxpy(-1.0, rV); }

      Vector<_ElemT>&
      AddScaled(_ElemT alpha, const Vector<_ElemT>& rV)
      { return BlasAxpy(alpha, rV); }

      Vector<_ElemT>&
      Add(_ElemT c);

      Vector<_ElemT>&
      MultiplyElements(const Vector<_ElemT>& rV);

      // @brief elementwise : rV.*rR+beta*this --> this
      Vector<_ElemT>&
      MultiplyElements(_ElemT alpha, const Vector<_ElemT>& rV, const Vector<_ElemT>& rR,_ElemT beta);

      Vector<_ElemT>&
      DivideElements(const Vector<_ElemT>& rV);

      /// @brief elementwise : rV./rR+beta*this --> this
      Vector<_ElemT>&
      DivideElements(_ElemT alpha, const Vector<_ElemT>& rV, const Vector<_ElemT>& rR,_ElemT beta);

      Vector<_ElemT>&
      Subtract(_ElemT c);

      Vector<_ElemT>&
      Scale(_ElemT c);

      //########################################################################
      //########################################################################

      /// Performs a row stack of the matrix rMa
      Vector<_ElemT>&
      MatrixRowStack(const Matrix<_ElemT>& rMa);

      // Extracts a row of the matrix rMa.  .. could also do this with vector.Copy(rMa[row]).
      Vector<_ElemT>&
      Row(const Matrix<_ElemT>& rMa, size_t row);

      // Extracts a column of the matrix rMa.
      Vector<_ElemT>&
      Col(const Matrix<_ElemT>& rMa, size_t col);

      // Takes all elements to a power.
      Vector<_ElemT>&
      Power(_ElemT power);

      _ElemT
      Max() const;

      _ElemT
      Min() const;

      /// Returns sum of the elements
      _ElemT
      Sum() const;

      /// Returns sum of the elements
      Vector<_ElemT>&
      AddRowSum(const Matrix<_ElemT>& rM);

      /// Returns sum of the elements
      Vector<_ElemT>&
      AddColSum(const Matrix<_ElemT>& rM);

      /// Returns log(sum(exp())) without exp overflow
      _ElemT
      LogSumExp() const;

      //########################################################################
      //########################################################################

      friend std::ostream &
      operator << <> (std::ostream& rOut, const Vector<_ElemT>& rV);

      friend _ElemT
      BlasDot<>(const Vector<_ElemT>& rA, const Vector<_ElemT>& rB);

      /**
       * Computes v1^T * M * v2.
       * Not as efficient as it could be where v1==v2 (but no suitable blas
       * routines available).
       */
      _ElemT
      InnerProduct(const Vector<_ElemT> &v1, const Matrix<_ElemT> &M, const Vector<_ElemT> &v2) const;

    //##########################################################################
    //##########################################################################
    //protected:
    public:
      /// data memory area
      _ElemT*   mpData;
#ifdef STK_MEMALIGN_MANUAL
      /// data to be freed (in case of manual memalignment use, see common.h)
      _ElemT*   mpFreeData;
#endif
      size_t  mDim;      ///< Number of elements
    }; // class Vector

  /**
   * @brief Represents a non-allocating general vector which can be defined
   * as a sub-vector of higher-level vector
   */
  template<typename _ElemT>
    class SubVector : public Vector<_ElemT>
    {
    protected:
      /// Constructor
      SubVector(const Vector<_ElemT>& rT,
                const size_t  origin,
                const size_t  length)
      {
        assert(origin+length <= rT.mDim);
        Vector<_ElemT>::mpData = rT.mpData+origin;
        Vector<_ElemT>::mDim   = length;
      }
      //only Vector class can call this protected constructor
      friend class Vector<_ElemT>;

    public:
      /// Constructor
      SubVector(Vector<_ElemT>& rT,
                const size_t  origin,
                const size_t  length)
      {
        assert(origin+length <= rT.mDim);
        Vector<_ElemT>::mpData = rT.mpData+origin;
        Vector<_ElemT>::mDim   = length;
      }

      /**
       * @brief Constructs a vector representation out of a standard array
       *
       * @param pData pointer to data array to associate with this vector
       * @param length length of this vector
       */
      inline
      SubVector(_ElemT *ppData,
                size_t length)
      {
        Vector<_ElemT>::mpData = ppData;
        Vector<_ElemT>::mDim   = length;
      }

      /**
       * @brief Destructor
       */
      ~SubVector()
      {
        Vector<_ElemT>::mpData = NULL;
      }
    };

    // Useful shortcuts
    typedef Vector<BaseFloat> BfVector;
    typedef SubVector<BaseFloat> BfSubVector;

    //Adding two vectors of different types
    template <typename _ElemT, typename _ElemU>
    void Add(Vector<_ElemT>& rDst, const Vector<_ElemU>& rSrc)
    {
      assert(rDst.Dim() == rSrc.Dim());
      const _ElemU* p_src = rSrc.pData();
      _ElemT* p_dst = rDst.pData();

      for(size_t i=0; i<rSrc.Dim(); i++) {
        *p_dst++ += (_ElemT)*p_src++;
      }
    }

    //Scales adding two vectors of different types
    template <typename _ElemT, typename _ElemU>
    void AddScaled(Vector<_ElemT>& rDst, const Vector<_ElemU>& rSrc, _ElemT scale)
    {
      assert(rDst.Dim() == rSrc.Dim());

      Vector<_ElemT> tmp(rSrc);
      rDst.BlasAxpy(scale, tmp);

/*
      const _ElemU* p_src = rSrc.pData();
      _ElemT* p_dst = rDst.pData();

      for(size_t i=0; i<rDst.Dim(); i++) {
        *p_dst++ += *p_src++ * scale;
      }
*/
    }

} // namespace TNet

//*****************************************************************************
//*****************************************************************************
// we need to include the implementation
#include "Vector.tcc"

/******************************************************************************
 ******************************************************************************
 * The following section contains specialized template definitions
 * whose implementation is in Vector.cc
 */

#endif // #ifndef TNet_Vector_h


	#pragma once

#include <DirectXMath.h>
#include <d3d11.h>
#include <gsl\gsl>

namespace Library
{
	class Mesh;

	template <typename T>
	struct VertexDeclaration
	{
		static constexpr uint32_t VertexSize() { return gsl::narrow_cast<uint32_t>(sizeof(T)); }
		static constexpr uint32_t VertexBufferByteWidth(size_t vertexCount) { return gsl::narrow_cast<uint32_t>(sizeof(T) * vertexCount); }
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const T>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer);
	};

	class VertexPosition : public VertexDeclaration<VertexPosition>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

	public:
		VertexPosition() = default;

		VertexPosition(const DirectX::XMFLOAT4& position) :
			Position(position) { }

		DirectX::XMFLOAT4 Position;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements { _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPosition>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexPositionColor : public VertexDeclaration<VertexPositionColor>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

	public:
		VertexPositionColor() = default;

		VertexPositionColor(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT4& color) :
			Position(position), Color(color) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT4 Color;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPositionColor>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexPositionTexture : public VertexDeclaration<VertexPositionTexture>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};
	public:
		VertexPositionTexture() = default;

		VertexPositionTexture(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT2& textureCoordinates) :
			Position(position), TextureCoordinates(textureCoordinates) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT2 TextureCoordinates;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPositionTexture>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexPositionSize : public VertexDeclaration<VertexPositionSize>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "SIZE", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

	public:
		VertexPositionSize() = default;

		VertexPositionSize(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT2& size) :
			Position(position), Size(size) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT2 Size;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPositionSize>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexPositionNormal : public VertexDeclaration<VertexPositionNormal>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

	public:
		VertexPositionNormal() = default;

		VertexPositionNormal(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT3& normal) :
			Position(position), Normal(normal) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT3 Normal;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPositionNormal>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexPositionTextureNormal : public VertexDeclaration<VertexPositionTextureNormal>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

	public:
		VertexPositionTextureNormal() = default;

		VertexPositionTextureNormal(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT2& textureCoordinates, const DirectX::XMFLOAT3& normal) :
			Position(position), TextureCoordinates(textureCoordinates), Normal(normal) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT2 TextureCoordinates;
		DirectX::XMFLOAT3 Normal;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPositionTextureNormal>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexPositionTextureNormalTangent : public VertexDeclaration<VertexPositionTextureNormalTangent>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

	public:
		VertexPositionTextureNormalTangent() = default;

		VertexPositionTextureNormalTangent(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT2& textureCoordinates, const DirectX::XMFLOAT3& normal, const DirectX::XMFLOAT3& tangent) :
			Position(position), TextureCoordinates(textureCoordinates), Normal(normal), Tangent(tangent) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT2 TextureCoordinates;
		DirectX::XMFLOAT3 Normal;
		DirectX::XMFLOAT3 Tangent;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexPositionTextureNormalTangent>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};

	class VertexSkinnedPositionTextureNormal : public VertexDeclaration<VertexSkinnedPositionTextureNormal>
	{
	private:
		inline static const D3D11_INPUT_ELEMENT_DESC _InputElements[]
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "BONEINDICES", 0, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "BONEWEIGHTS", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
		};

	public:
		VertexSkinnedPositionTextureNormal() = default;

		VertexSkinnedPositionTextureNormal(const DirectX::XMFLOAT4& position, const DirectX::XMFLOAT2& textureCoordinates, const DirectX::XMFLOAT3& normal, const DirectX::XMUINT4& boneIndices, const DirectX::XMFLOAT4& boneWeights) :
			Position(position), TextureCoordinates(textureCoordinates), Normal(normal), BoneIndices(boneIndices), BoneWeights(boneWeights) { }

		DirectX::XMFLOAT4 Position;
		DirectX::XMFLOAT2 TextureCoordinates;
		DirectX::XMFLOAT3 Normal;
		DirectX::XMUINT4 BoneIndices;
		DirectX::XMFLOAT4 BoneWeights;

		inline static const gsl::span<const D3D11_INPUT_ELEMENT_DESC> InputElements{ _InputElements };

		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const Library::Mesh& mesh, gsl::not_null<ID3D11Buffer**> vertexBuffer);
		static void CreateVertexBuffer(gsl::not_null<ID3D11Device*> device, const gsl::span<const VertexSkinnedPositionTextureNormal>& vertices, gsl::not_null<ID3D11Buffer**> vertexBuffer)
		{
			VertexDeclaration::CreateVertexBuffer(device, vertices, vertexBuffer);
		}
	};
}

#include "VertexDeclarations.inl"


/*
* -----------------------------------------------------------------
*  pmsr_lib.h
*  Pairwise Mixing Stirred Reactor Library
*  Version: 2.0
*  Last Update: Oct 1, 2019
*
*  This is the header file of a computational library with
*  routines to implement a Pairwise Mixing Stirred Reactor
*  (PMSR) model.
* -----------------------------------------------------------------
*  Programmer: Americo Barbosa da Cunha Junior
*              americo.cunhajr@gmail.com
* -----------------------------------------------------------------
*  Copyright (c) 2019 by Americo Barbosa da Cunha Junior
* -----------------------------------------------------------------
*/

#ifndef __PMSR_LIB_H__
#define __PMSR_LIB_H__

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

/*
* ------------------------------------------------------------
*  Types: struct pmsr, pmsr_wrk
* ------------------------------------------------------------
* This structure contains fields of PMSR model.
*
* last update: Nov 5, 2009
* ------------------------------------------------------------
*/

typedef struct pmsr
{
    int *pmsr_idx;         /* particles index vector */
    gsl_vector **pmsr_ps;  /* particle system        */
} pmsr_wrk;
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
* function prototypes
*------------------------------------------------------------
*/

void pmsr_title();

int pmsr_input(unsigned long int *seed,
               unsigned int *Np,
               unsigned int *Ndt,
	       double *t0,
               double *delta_t,
               double *tau_res,
               double *tau_mix,
	       double *tau_pair,
               double *Tmax,
               double *Tmin,
               double *atol,
               double *rtol);

pmsr_wrk *pmsr_alloc();

int pmsr_init(int Np,
              int Neq,
              pmsr_wrk *pmsr);

void pmsr_free(int Np,
               void **pmsr_bl);

void pmsr_set_all(int Np,
                  gsl_vector *phi,
                  pmsr_wrk *pmsr);

int pmsr_Nexc(int Np,
              double delta_t,
              double tau_res);

int pmsr_Npair(int Np,
               double delta_t,
               double tau_pair);

void pmsr_meanvar(int Np,
                  int k,
                  gsl_vector **ps,
                  double *mean,
                  double *var);

void pmsr_mixture(int Np,
                  int Neq,
                  double delta_t,
                  double tau_mix,
                  int *idx,
                  gsl_vector **ps);

void pmsr_iops(int Np,
               int Nexc,
               int Npair,
               gsl_rng *r,
               gsl_vector *phi,
               pmsr_wrk *pmsr);

#endif /* __PMSR_LIB_H__ */


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include <iosfwd>
#include <gsl\gsl>
#include <vector>

#include "Data\Data.h"
#include "Data\TimeUnits.h"

#include <Plat/CameraDevice/CameraSettings.h>

namespace mage
{
    namespace binary_data
    {
        constexpr uint64_t LATEST_BINARY_VERSION = 3;
        constexpr uint64_t LATEST_CALIBRATION_VERSION = 2;

        // portable strings for device information
        struct DeviceInformation
        {
            std::string Id;
            std::string DeviceName;
            std::string DeviceManufacturer;
            std::string DeviceHardwareVersion;
            std::string DeviceFirmwareVersion;
        };

        struct HeaderData
        {
            uint64_t BinaryVersion;
            uint64_t CalibrationVersion;
            calibration::LinearFocalLengthModel Calibration;
            mage::Size ImageSize;
            mage::PixelFormat FormatOfPixels;

            DeviceInformation DeviceInformation;

            HeaderData(const uint64_t& b, const uint64_t& m, const calibration::LinearFocalLengthModel& c, const mage::Size& size, const mage::PixelFormat &fp, const mage::binary_data::DeviceInformation& deviceInfo)
                : BinaryVersion{ b }, CalibrationVersion{ m }, Calibration{ c }, ImageSize{ size }, FormatOfPixels{ fp }, DeviceInformation{ deviceInfo }
            {}

            HeaderData()
                : BinaryVersion{ LATEST_BINARY_VERSION },
                CalibrationVersion{ LATEST_CALIBRATION_VERSION },
                Calibration{},
                ImageSize{},
                FormatOfPixels{ mage::PixelFormat::GRAYSCALE8 },
                DeviceInformation{ "n/a", "n/a", "n/a", "n/a" }
            {}
        };

        struct FileFrameData
        {
            uint64_t CorrelationId;
            hundred_nanoseconds TimeStamp;
            mira::CameraSettings CameraSettings;
            std::vector<uint8_t> Pixels;

            FileFrameData(const uint64_t& correlationId, const hundred_nanoseconds& t, const mira::CameraSettings& cameraSettings, std::vector<uint8_t> p)
                : CorrelationId{ correlationId }, TimeStamp{ t }, CameraSettings{ cameraSettings }, Pixels{ std::move(p) }
            {}
            FileFrameData() = default;
            FileFrameData(FileFrameData&&) = default;
        };

        void SerializeCaptureHeaderData(std::fstream& outputFile, const HeaderData& data);
        void SerializeFrameData(std::fstream& outputFile, const FileFrameData& data);

        bool DeSerializeHeaderData(std::istream& inputFile, HeaderData& data);
        FileFrameData DeSerializeFrameData(std::istream& inputFile, HeaderData& headerData);
    }
}


#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_math.h>
#include "gsl_cblas.h"

#include "tests.h"

void
test_gbmv (void) {
const double flteps = 1e-4, dbleps = 1e-6;
  {
   int order = 101;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha = -1.0f;
   float beta = -1.0f;
   float A[] = { 0.423f, -0.143f, -0.182f, -0.076f, -0.855f, 0.599f, 0.389f, -0.473f, 0.493f, -0.902f, -0.889f, -0.256f, 0.112f, 0.128f, -0.277f, -0.777f };
   float X[] = { 0.488f, 0.029f, -0.633f, 0.84f };
   int incX = -1;
   float Y[] = { 0.874f, 0.322f, -0.477f };
   int incY = -1;
   float y_expected[] = { -0.101941f, 0.764086f, 0.481914f };
   cblas_sgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "sgbmv(case 794)");
     }
   };
  };

  {
   int order = 102;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha = -1.0f;
   float beta = -1.0f;
   float A[] = { 0.423f, -0.143f, -0.182f, -0.076f, -0.855f, 0.599f, 0.389f, -0.473f, 0.493f, -0.902f, -0.889f, -0.256f, 0.112f, 0.128f, -0.277f, -0.777f };
   float X[] = { 0.488f, 0.029f, -0.633f, 0.84f };
   int incX = -1;
   float Y[] = { 0.874f, 0.322f, -0.477f };
   int incY = -1;
   float y_expected[] = { -0.656261f, 0.19575f, 0.055905f };
   cblas_sgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "sgbmv(case 795)");
     }
   };
  };

  {
   int order = 101;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha = 0.0f;
   float beta = 0.1f;
   float A[] = { -0.066f, -0.153f, -0.619f, 0.174f, 0.777f, 0.543f, 0.614f, -0.446f, -0.138f, -0.767f, 0.725f, 0.222f, 0.165f, -0.063f, -0.047f, 0.267f };
   float X[] = { -0.096f, -0.007f, -0.657f };
   int incX = -1;
   float Y[] = { -0.88f, 0.102f, -0.278f, 0.403f };
   int incY = -1;
   float y_expected[] = { -0.088f, 0.0102f, -0.0278f, 0.0403f };
   cblas_sgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "sgbmv(case 796)");
     }
   };
  };

  {
   int order = 102;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha = 0.0f;
   float beta = 0.1f;
   float A[] = { -0.066f, -0.153f, -0.619f, 0.174f, 0.777f, 0.543f, 0.614f, -0.446f, -0.138f, -0.767f, 0.725f, 0.222f, 0.165f, -0.063f, -0.047f, 0.267f };
   float X[] = { -0.096f, -0.007f, -0.657f };
   int incX = -1;
   float Y[] = { -0.88f, 0.102f, -0.278f, 0.403f };
   int incY = -1;
   float y_expected[] = { -0.088f, 0.0102f, -0.0278f, 0.0403f };
   cblas_sgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "sgbmv(case 797)");
     }
   };
  };

  {
   int order = 101;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha = 0.1;
   double beta = 0;
   double A[] = { -0.688, 0.29, 0.442, -0.001, 0.313, -0.073, 0.991, -0.654, -0.12, 0.416, 0.571, 0.932, -0.179, -0.724, 0.492, -0.965 };
   double X[] = { 0.187, -0.338, -0.976, -0.052 };
   int incX = -1;
   double Y[] = { -0.101, 0.8, 0.026 };
   int incY = -1;
   double y_expected[] = { 0.0083289, -0.0279986, -0.0446472 };
   cblas_dgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dgbmv(case 798)");
     }
   };
  };

  {
   int order = 102;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha = 0.1;
   double beta = 0;
   double A[] = { -0.688, 0.29, 0.442, -0.001, 0.313, -0.073, 0.991, -0.654, -0.12, 0.416, 0.571, 0.932, -0.179, -0.724, 0.492, -0.965 };
   double X[] = { 0.187, -0.338, -0.976, -0.052 };
   int incX = -1;
   double Y[] = { -0.101, 0.8, 0.026 };
   int incY = -1;
   double y_expected[] = { -0.1141297, 0.0088824, -0.0320568 };
   cblas_dgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dgbmv(case 799)");
     }
   };
  };

  {
   int order = 101;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha = -0.3;
   double beta = -0.3;
   double A[] = { 0.746, 0.262, -0.449, -0.954, -0.093, 0.108, -0.496, 0.927, 0.177, 0.729, -0.92, -0.469, 0.87, -0.877, -0.308, -0.806 };
   double X[] = { 0.662, -0.887, 0.261 };
   int incX = -1;
   double Y[] = { 0.771, 0.637, -0.177, -0.018 };
   int incY = -1;
   double y_expected[] = { -0.048588, -0.467865, 0.0818433, -0.0398619 };
   cblas_dgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dgbmv(case 800)");
     }
   };
  };

  {
   int order = 102;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha = -0.3;
   double beta = -0.3;
   double A[] = { 0.746, 0.262, -0.449, -0.954, -0.093, 0.108, -0.496, 0.927, 0.177, 0.729, -0.92, -0.469, 0.87, -0.877, -0.308, -0.806 };
   double X[] = { 0.662, -0.887, 0.261 };
   int incX = -1;
   double Y[] = { 0.771, 0.637, -0.177, -0.018 };
   int incY = -1;
   double y_expected[] = { -0.404082, -0.2887797, 0.1876263, -0.1345935 };
   cblas_dgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dgbmv(case 801)");
     }
   };
  };

  {
   int order = 101;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha[2] = {0.0f, 1.0f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.107f, 0.926f, -0.246f, -0.555f, -0.301f, 0.276f, 0.471f, -0.084f, -0.754f, 0.082f, -0.952f, -0.394f, 0.659f, 0.054f, 0.795f, 0.923f, 0.232f, -0.788f, 0.478f, 0.775f, -0.118f, 0.691f, -0.933f, 0.809f, 0.164f, -0.263f, -0.923f, -0.88f, 0.819f, -0.521f, -0.045f, 0.034f };
   float X[] = { 0.407f, 0.895f, 0.301f, 0.769f, -0.269f, -0.465f, 0.455f, -0.628f };
   int incX = -1;
   float Y[] = { -0.116f, -0.744f, -0.936f, -0.064f, -0.232f, -0.665f };
   int incY = -1;
   float y_expected[] = { -0.806176f, -1.559f, -1.57611f, -0.155463f, 0.098816f, -0.274361f };
   cblas_cgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], flteps, "cgbmv(case 802) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], flteps, "cgbmv(case 802) imag");
     };
   };
  };

  {
   int order = 102;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha[2] = {0.0f, 1.0f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.107f, 0.926f, -0.246f, -0.555f, -0.301f, 0.276f, 0.471f, -0.084f, -0.754f, 0.082f, -0.952f, -0.394f, 0.659f, 0.054f, 0.795f, 0.923f, 0.232f, -0.788f, 0.478f, 0.775f, -0.118f, 0.691f, -0.933f, 0.809f, 0.164f, -0.263f, -0.923f, -0.88f, 0.819f, -0.521f, -0.045f, 0.034f };
   float X[] = { 0.407f, 0.895f, 0.301f, 0.769f, -0.269f, -0.465f, 0.455f, -0.628f };
   int incX = -1;
   float Y[] = { -0.116f, -0.744f, -0.936f, -0.064f, -0.232f, -0.665f };
   int incY = -1;
   float y_expected[] = { -0.245235f, -0.313725f, -0.798094f, 0.691455f, -0.164015f, -0.242714f };
   cblas_cgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], flteps, "cgbmv(case 803) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], flteps, "cgbmv(case 803) imag");
     };
   };
  };

  {
   int order = 101;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.258f, 0.838f, -0.106f, -0.066f, 0.395f, 0.982f, -0.546f, 0.565f, 0.14f, -0.18f, 0.165f, -0.186f, 0.499f, -0.038f, -0.305f, -0.653f, -0.811f, -0.466f, -0.674f, -0.013f, -0.552f, -0.807f, -0.536f, 0.864f, -0.027f, -0.606f, 0.459f, 0.564f, -0.968f, 0.717f, -0.312f, -0.485f };
   float X[] = { -0.399f, 0.459f, 0.398f, 0.358f, -0.161f, -0.359f };
   int incX = -1;
   float Y[] = { 0.572f, 0.293f, -0.813f, -0.096f, -0.611f, -0.717f, 0.736f, 0.259f };
   int incY = -1;
   float y_expected[] = { -0.619961f, -0.011425f, -0.477499f, 0.059361f, -0.886984f, 0.44008f, -0.139432f, 0.04644f };
   cblas_cgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], flteps, "cgbmv(case 804) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], flteps, "cgbmv(case 804) imag");
     };
   };
  };

  {
   int order = 102;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.258f, 0.838f, -0.106f, -0.066f, 0.395f, 0.982f, -0.546f, 0.565f, 0.14f, -0.18f, 0.165f, -0.186f, 0.499f, -0.038f, -0.305f, -0.653f, -0.811f, -0.466f, -0.674f, -0.013f, -0.552f, -0.807f, -0.536f, 0.864f, -0.027f, -0.606f, 0.459f, 0.564f, -0.968f, 0.717f, -0.312f, -0.485f };
   float X[] = { -0.399f, 0.459f, 0.398f, 0.358f, -0.161f, -0.359f };
   int incX = -1;
   float Y[] = { 0.572f, 0.293f, -0.813f, -0.096f, -0.611f, -0.717f, 0.736f, 0.259f };
   int incY = -1;
   float y_expected[] = { -0.318227f, -0.172201f, -0.109343f, 0.698685f, 0.208261f, -0.269065f, 0.175074f, -0.507326f };
   cblas_cgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], flteps, "cgbmv(case 805) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], flteps, "cgbmv(case 805) imag");
     };
   };
  };

  {
   int order = 101;
   int trans = 113;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.804f, 0.232f, -0.448f, -0.558f, -0.078f, -0.056f, -0.345f, -0.379f, 0.369f, -0.662f, -0.169f, -0.391f, -0.215f, 0.467f, 0.374f, 0.889f, -0.698f, 0.734f, 0.377f, -0.955f, 0.498f, 0.151f, -0.725f, -0.728f, -0.655f, -0.581f, 0.389f, 0.949f, -0.553f, -0.434f, 0.237f, 0.641f };
   float X[] = { -0.262f, -0.823f, -0.357f, -0.994f, -0.347f, -0.375f };
   int incX = -1;
   float Y[] = { -0.683f, -0.87f, -0.708f, 0.071f, 0.575f, -0.575f, 0.845f, 0.032f };
   int incY = -1;
   float y_expected[] = { 0.341749f, 0.301992f, -0.306848f, 0.109252f, -0.018347f, -0.747479f, -0.894201f, 0.713246f };
   cblas_cgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], flteps, "cgbmv(case 806) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], flteps, "cgbmv(case 806) imag");
     };
   };
  };

  {
   int order = 102;
   int trans = 113;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.804f, 0.232f, -0.448f, -0.558f, -0.078f, -0.056f, -0.345f, -0.379f, 0.369f, -0.662f, -0.169f, -0.391f, -0.215f, 0.467f, 0.374f, 0.889f, -0.698f, 0.734f, 0.377f, -0.955f, 0.498f, 0.151f, -0.725f, -0.728f, -0.655f, -0.581f, 0.389f, 0.949f, -0.553f, -0.434f, 0.237f, 0.641f };
   float X[] = { -0.262f, -0.823f, -0.357f, -0.994f, -0.347f, -0.375f };
   int incX = -1;
   float Y[] = { -0.683f, -0.87f, -0.708f, 0.071f, 0.575f, -0.575f, 0.845f, 0.032f };
   int incY = -1;
   float y_expected[] = { -0.562773f, -0.455143f, -0.213881f, -0.466169f, -0.183683f, 0.097891f, -0.451416f, 0.052586f };
   cblas_cgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], flteps, "cgbmv(case 807) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], flteps, "cgbmv(case 807) imag");
     };
   };
  };

  {
   int order = 101;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha[2] = {0, 0.1};
   double beta[2] = {1, 0};
   double A[] = { -0.919, -0.002, 0.105, -0.338, -0.358, -0.715, -0.157, 0.307, 0.334, 0.121, 0.366, 0.029, -0.006, -0.662, -0.314, 0.061, -0.322, -0.865, -0.586, 0.556, 0.507, 0.581, 0.855, -0.09, 0.836, -0.788, -0.209, -0.694, -0.695, 0.11, -0.234, 0.17 };
   double X[] = { 0.356, -0.76, -0.96, 0.437, -0.849, 0.397, -0.382, -0.826 };
   int incX = -1;
   double Y[] = { 0.288, -0.832, 0.889, 0.576, -0.809, 0.4 };
   int incY = -1;
   double y_expected[] = { 0.3241775, -0.6761577, 0.8458527, 0.5705165, -0.8597295, 0.4268499 };
   cblas_zgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], dbleps, "zgbmv(case 808) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], dbleps, "zgbmv(case 808) imag");
     };
   };
  };

  {
   int order = 102;
   int trans = 111;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha[2] = {0, 0.1};
   double beta[2] = {1, 0};
   double A[] = { -0.919, -0.002, 0.105, -0.338, -0.358, -0.715, -0.157, 0.307, 0.334, 0.121, 0.366, 0.029, -0.006, -0.662, -0.314, 0.061, -0.322, -0.865, -0.586, 0.556, 0.507, 0.581, 0.855, -0.09, 0.836, -0.788, -0.209, -0.694, -0.695, 0.11, -0.234, 0.17 };
   double X[] = { 0.356, -0.76, -0.96, 0.437, -0.849, 0.397, -0.382, -0.826 };
   int incX = -1;
   double Y[] = { 0.288, -0.832, 0.889, 0.576, -0.809, 0.4 };
   int incY = -1;
   double y_expected[] = { 0.4026074, -0.8033768, 0.7510795, 0.5671044, -0.8162255, 0.3349099 };
   cblas_zgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 3; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], dbleps, "zgbmv(case 809) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], dbleps, "zgbmv(case 809) imag");
     };
   };
  };

  {
   int order = 101;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha[2] = {1, 0};
   double beta[2] = {1, 0};
   double A[] = { 0.511, -0.707, -0.906, 0.345, -0.524, -0.933, 0.154, -0.529, -0.651, -0.851, 0.104, 0.532, -0.297, 0.477, 0.511, 0.469, -0.888, -0.789, 0.656, 0.288, -0.749, 0.961, 0.571, 0.539, 0.465, 0.647, 0.653, -0.994, -0.515, 0.297, 0.35, -0.707 };
   double X[] = { -0.991, 0.658, -0.909, -0.99, -0.517, -0.071 };
   int incX = -1;
   double Y[] = { 0.451, 0.351, -0.113, -0.62, 0.983, 0.511, 0.142, -0.186 };
   int incY = -1;
   double y_expected[] = { 0.560921, -1.094193, -0.210397, -0.613323, 3.018979, 0.641612, 0.384166, 1.11801 };
   cblas_zgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], dbleps, "zgbmv(case 810) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], dbleps, "zgbmv(case 810) imag");
     };
   };
  };

  {
   int order = 102;
   int trans = 112;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha[2] = {1, 0};
   double beta[2] = {1, 0};
   double A[] = { 0.511, -0.707, -0.906, 0.345, -0.524, -0.933, 0.154, -0.529, -0.651, -0.851, 0.104, 0.532, -0.297, 0.477, 0.511, 0.469, -0.888, -0.789, 0.656, 0.288, -0.749, 0.961, 0.571, 0.539, 0.465, 0.647, 0.653, -0.994, -0.515, 0.297, 0.35, -0.707 };
   double X[] = { -0.991, 0.658, -0.909, -0.99, -0.517, -0.071 };
   int incX = -1;
   double Y[] = { 0.451, 0.351, -0.113, -0.62, 0.983, 0.511, 0.142, -0.186 };
   int incY = -1;
   double y_expected[] = { -0.435541, 0.015793, -0.926518, 1.122561, 1.671751, -0.257493, 0.187543, 1.066818 };
   cblas_zgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], dbleps, "zgbmv(case 811) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], dbleps, "zgbmv(case 811) imag");
     };
   };
  };

  {
   int order = 101;
   int trans = 113;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha[2] = {0, 0.1};
   double beta[2] = {-0.3, 0.1};
   double A[] = { 0.534, 0.67, -0.621, 0.143, -0.794, 0.073, 0.414, -0.9, 0.155, -0.368, 0.122, -0.583, 0.03, 0.646, -0.768, -0.892, -0.741, -0.397, 0.626, 0.004, -0.515, 0.355, 0.196, -0.989, -0.982, 0.985, 0.445, 0.63, -0.849, -0.528, 0.146, -0.319 };
   double X[] = { -0.199, -0.259, 0.386, -0.131, -0.867, 0.888 };
   int incX = -1;
   double Y[] = { 0.106, 0.874, 0.962, 0.636, -0.759, 0.415, -0.053, 0.315 };
   int incY = -1;
   double y_expected[] = { -0.139603, -0.250546, -0.3107376, -0.1144656, 0.2181809, -0.0877031, 0.0149724, -0.0224571 };
   cblas_zgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], dbleps, "zgbmv(case 812) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], dbleps, "zgbmv(case 812) imag");
     };
   };
  };

  {
   int order = 102;
   int trans = 113;
   int M = 3;
   int N = 4;
   int KL = 1;
   int KU = 1;
   int lda = 4;
   double alpha[2] = {0, 0.1};
   double beta[2] = {-0.3, 0.1};
   double A[] = { 0.534, 0.67, -0.621, 0.143, -0.794, 0.073, 0.414, -0.9, 0.155, -0.368, 0.122, -0.583, 0.03, 0.646, -0.768, -0.892, -0.741, -0.397, 0.626, 0.004, -0.515, 0.355, 0.196, -0.989, -0.982, 0.985, 0.445, 0.63, -0.849, -0.528, 0.146, -0.319 };
   double X[] = { -0.199, -0.259, 0.386, -0.131, -0.867, 0.888 };
   int incX = -1;
   double Y[] = { 0.106, 0.874, 0.962, 0.636, -0.759, 0.415, -0.053, 0.315 };
   int incY = -1;
   double y_expected[] = { -0.1642353, -0.2575697, -0.3610975, -0.1305629, 0.1713576, -0.2514988, 0.0195631, -0.0648656 };
   cblas_zgbmv(order, trans, M, N, KU, KL, alpha, A,                                  lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(Y[2*i], y_expected[2*i], dbleps, "zgbmv(case 813) real");
       gsl_test_rel(Y[2*i+1], y_expected[2*i+1], dbleps, "zgbmv(case 813) imag");
     };
   };
  };

}


// --------------------------------------------------------------------------
//                   OpenMS -- Open-Source Mass Spectrometry
// --------------------------------------------------------------------------
// Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
// ETH Zurich, and Freie Universitaet Berlin 2002-2013.
//
// This software is released under a three-clause BSD license:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * 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.
//  * Neither the name of any author or any participating institution
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
// For a full list of authors, refer to the file AUTHORS.
// --------------------------------------------------------------------------
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ANY OF THE AUTHORS OR THE CONTRIBUTING
// INSTITUTIONS 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.
//
// --------------------------------------------------------------------------
// $Maintainer: Clemens Groepl $
// $Authors: $
// --------------------------------------------------------------------------

#ifndef OPENMS_TRANSFORMATIONS_FEATUREFINDER_LEVMARQFITTER1D_H
#define OPENMS_TRANSFORMATIONS_FEATUREFINDER_LEVMARQFITTER1D_H

#include <OpenMS/TRANSFORMATIONS/FEATUREFINDER/Fitter1D.h>

#include <gsl/gsl_rng.h> // gsl random number generators
#include <gsl/gsl_randist.h> // gsl random number distributions
#include <gsl/gsl_vector.h> // gsl vector and matrix definitions
#include <gsl/gsl_multifit_nlin.h> // gsl multidimensional fitting
#include <gsl/gsl_blas.h> // gsl linear algebra stuff

#include <OpenMS/DATASTRUCTURES/ListUtils.h>

namespace OpenMS
{

  /**
    @brief Abstract class for 1D-model fitter using Levenberg-Marquardt algorithm for parameter optimization.
      */
  class OPENMS_DLLAPI LevMarqFitter1D :
    public Fitter1D
  {

public:

    typedef std::vector<double> ContainerType;

    /// Default constructor
    LevMarqFitter1D() :
      Fitter1D()
    {
      this->defaults_.setValue("max_iteration", 500, "Maximum number of iterations using by Levenberg-Marquardt algorithm.", ListUtils::create<String>("advanced"));
      this->defaults_.setValue("deltaAbsError", 0.0001, "Absolute error used by the Levenberg-Marquardt algorithm.", ListUtils::create<String>("advanced"));
      this->defaults_.setValue("deltaRelError", 0.0001, "Relative error used by the Levenberg-Marquardt algorithm.", ListUtils::create<String>("advanced"));
    }

    /// copy constructor
    LevMarqFitter1D(const LevMarqFitter1D & source) :
      Fitter1D(source),
      max_iteration_(source.max_iteration_),
      abs_error_(source.abs_error_),
      rel_error_(source.rel_error_)
    {
    }

    /// destructor
    virtual ~LevMarqFitter1D()
    {
    }

    /// assignment operator
    virtual LevMarqFitter1D & operator=(const LevMarqFitter1D & source)
    {
      if (&source == this) return *this;

      Fitter1D::operator=(source);
      max_iteration_ = source.max_iteration_;
      abs_error_ = source.abs_error_;
      rel_error_ = source.rel_error_;

      return *this;
    }

protected:

    /// GSL status
    Int gsl_status_;
    /// Parameter indicates symmetric peaks
    bool symmetric_;
    /// Maximum number of iterations
    Int max_iteration_;
    /** Test for the convergence of the sequence by comparing the last iteration step dx with the absolute error epsabs and relative error epsrel to the current position x */
    /// Absolute error
    CoordinateType abs_error_;
    /// Relative error
    CoordinateType rel_error_;

    /** Display the intermediate state of the solution. The solver state contains
        the vector s->x which is the current position, and the vector s->f with
        corresponding function values */
    virtual void printState_(Int iter, gsl_multifit_fdfsolver * s) = 0;

    /// Return GSL status as string
    const String getGslStatus_()
    {
      return gsl_strerror(gsl_status_);
    }

    /**
        @brief Optimize start parameter

        @exception Exception::UnableToFit is thrown if fitting cannot be performed
    */
    void optimize_(const RawDataArrayType & set, Int num_params, CoordinateType x_init[],
                   Int (* residual)(const gsl_vector * x, void * params, gsl_vector * f),
                   Int (* jacobian)(const gsl_vector * x, void * params, gsl_matrix * J),
                   Int (* evaluate)(const gsl_vector * x, void * params, gsl_vector * f, gsl_matrix * J),
                   void * advanced_params
                   )
    {

      const gsl_multifit_fdfsolver_type * T;
      gsl_multifit_fdfsolver * s;

      Int status;
      Int iter = 0;
      const UInt n = (UInt)set.size();

      // number of parameters to be optimized
      UInt p = num_params;

      // gsl always expects N>=p or default gsl error handler invoked,
      // cause Jacobian be rectangular M x N with M>=N
      if (n < p) throw Exception::UnableToFit(__FILE__, __LINE__, __PRETTY_FUNCTION__, "UnableToFit-FinalSet", "Skipping feature, gsl always expects N>=p");

      // allocate space for a covariance matrix of size p by p
      gsl_matrix * covar = gsl_matrix_alloc(p, p);
      gsl_multifit_function_fdf f;

      gsl_vector_view x = gsl_vector_view_array(x_init, p);

      gsl_rng_env_setup();

      // set up the function to be fit
      f.f = (residual);     // the function of residuals
      f.df = (jacobian);     // the gradient of this function
      f.fdf = (evaluate);     // combined function and gradient
      f.n = set.size();     // number of points in the data set
      f.p = p;     // number of parameters in the fit function
      f.params = advanced_params;     // // structure with the data and error bars

      T = gsl_multifit_fdfsolver_lmsder;
      s = gsl_multifit_fdfsolver_alloc(T, n, p);
      gsl_multifit_fdfsolver_set(s, &f, &x.vector);

#ifdef DEBUG_FEATUREFINDER
      printState_(iter, s);
#endif

      // this is the loop for fitting
      do
      {
        iter++;

        // perform a single iteration of the fitting routine
        status = gsl_multifit_fdfsolver_iterate(s);

#ifdef DEBUG_FEATUREFINDER
        // customized routine to print out current parameters
        printState_(iter, s);
#endif

        /* check if solver is stuck */
        if (status) break;

        // test for convergence with an absolute and relative error
        status = gsl_multifit_test_delta(s->dx, s->x, abs_error_, rel_error_);
      }
      while (status == GSL_CONTINUE && iter < max_iteration_);

      // This function uses Jacobian matrix J to compute the covariance matrix of the best-fit parameters, covar.
      // The parameter epsrel (0.0) is used to remove linear-dependent columns when J is rank deficient.
      gsl_multifit_covar(s->J, 0.0, covar);

#ifdef DEBUG_FEATUREFINDER
      gsl_matrix_fprintf(stdout, covar, "covar %g");
#endif

#define FIT(i) gsl_vector_get(s->x, i)
#define ERR(i) sqrt(gsl_matrix_get(covar, i, i))

      // Set GSl status
      gsl_status_ = status;

#ifdef DEBUG_FEATUREFINDER
      {
        // chi-squared value
        DoubleReal chi = gsl_blas_dnrm2(s->f);
        DoubleReal dof = n - p;
        DoubleReal c = GSL_MAX_DBL(1, chi / sqrt(dof));

        printf("chisq/dof = %g\n", pow(chi, 2.0) / dof);

        for (Size i = 0; i < p; ++i)
        {
          std::cout << i;
          printf(".Parameter = %.5f +/- %.5f\n", FIT(i), c * ERR(i));
        }
      }
#endif

      // set optimized parameters
      for (Size i = 0; i < p; ++i)
      {
        x_init[i] = FIT(i);
      }

      gsl_multifit_fdfsolver_free(s);
      gsl_matrix_free(covar);

    }

    void updateMembers_()
    {
      Fitter1D::updateMembers_();
      max_iteration_ = this->param_.getValue("max_iteration");
      abs_error_ = this->param_.getValue("deltaAbsError");
      rel_error_ = this->param_.getValue("deltaRelError");
    }

  };
}

#endif // OPENMS_TRANSFORMATIONS_FEATUREFINDER_LEVMARQFITTER1D_H