#pragma once
#include "BufferView.h"
#include "Enum.h"
#include "Error.h"
#include "parsers/Parsing.h"
#include <boost/hana/define_struct.hpp>
#include <gsl/span>

namespace gltfpp {
	inline namespace v1 {
		BETTER_ENUM(AccessorComponentType,
		            int,
		            BYTE = 5120,
		            UNSIGNED_BYTE = 5121,
		            SHORT = 5122,
		            UNSIGNED_SHORT = 5123,
		            UNSIGNED_INT = 5125,
		            FLOAT = 5126)

		BETTER_ENUM(AccessorType, int, SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4)

		constexpr auto AccessorTypeComponentCount =
		    boost::hana::make_map(boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::SCALAR>, 1),
		                          boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::VEC2>, 2),
		                          boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::VEC3>, 3),
		                          boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::VEC4>, 4),
		                          boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::MAT2>, 4),
		                          boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::MAT3>, 9),
		                          boost::hana::make_pair(boost::hana::int_c<(int)AccessorType::MAT4>, 16));

		struct Accessor {
			BOOST_HANA_DEFINE_STRUCT(Accessor,
			                         (bool, normalized),
			                         (std::vector<double>, min),
			                         (std::vector<double>, max),
			                         (option<std::string>, name),
			                         (option<nlohmann::json>, sparse),
			                         (option<nlohmann::json>, extensions),
			                         (option<nlohmann::json>, extras),
			                         (ptrdiff_t, byteOffset),
			                         (size_t, count),
			                         (AccessorComponentType, componentType));
			BufferView const *bufferView;
		};

		auto parse(Accessor &) noexcept;
	}    // namespace v1
}    // namespace gltfpp


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

#ifndef __GSL_VECTOR_SHORT_H__
#define __GSL_VECTOR_SHORT_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_short.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  short *data;
  gsl_block_short *block;
  int owner;
}
gsl_vector_short;

typedef struct
{
  gsl_vector_short vector;
} _gsl_vector_short_view;

typedef _gsl_vector_short_view gsl_vector_short_view;

typedef struct
{
  gsl_vector_short vector;
} _gsl_vector_short_const_view;

typedef const _gsl_vector_short_const_view gsl_vector_short_const_view;

/* Allocation */

GSL_FUN gsl_vector_short *gsl_vector_short_alloc (const size_t n);
GSL_FUN gsl_vector_short *gsl_vector_short_calloc (const size_t n);

GSL_FUN gsl_vector_short *gsl_vector_short_alloc_from_block (gsl_block_short * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

GSL_FUN gsl_vector_short *gsl_vector_short_alloc_from_vector (gsl_vector_short * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

GSL_FUN void gsl_vector_short_free (gsl_vector_short * v);

/* Views */

GSL_FUN _gsl_vector_short_view
gsl_vector_short_view_array (short *v, size_t n);

GSL_FUN _gsl_vector_short_view
gsl_vector_short_view_array_with_stride (short *base,
                                         size_t stride,
                                         size_t n);

GSL_FUN _gsl_vector_short_const_view
gsl_vector_short_const_view_array (const short *v, size_t n);

GSL_FUN _gsl_vector_short_const_view
gsl_vector_short_const_view_array_with_stride (const short *base,
                                               size_t stride,
                                               size_t n);

GSL_FUN _gsl_vector_short_view
gsl_vector_short_subvector (gsl_vector_short *v,
                            size_t i,
                            size_t n);

GSL_FUN _gsl_vector_short_view
gsl_vector_short_subvector_with_stride (gsl_vector_short *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_FUN _gsl_vector_short_const_view
gsl_vector_short_const_subvector (const gsl_vector_short *v,
                                  size_t i,
                                  size_t n);

GSL_FUN _gsl_vector_short_const_view
gsl_vector_short_const_subvector_with_stride (const gsl_vector_short *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_FUN void gsl_vector_short_set_zero (gsl_vector_short * v);
GSL_FUN void gsl_vector_short_set_all (gsl_vector_short * v, short x);
GSL_FUN int gsl_vector_short_set_basis (gsl_vector_short * v, size_t i);

GSL_FUN int gsl_vector_short_fread (FILE * stream, gsl_vector_short * v);
GSL_FUN int gsl_vector_short_fwrite (FILE * stream, const gsl_vector_short * v);
GSL_FUN int gsl_vector_short_fscanf (FILE * stream, gsl_vector_short * v);
GSL_FUN int gsl_vector_short_fprintf (FILE * stream, const gsl_vector_short * v,
                              const char *format);

GSL_FUN int gsl_vector_short_memcpy (gsl_vector_short * dest, const gsl_vector_short * src);

GSL_FUN int gsl_vector_short_reverse (gsl_vector_short * v);

GSL_FUN int gsl_vector_short_swap (gsl_vector_short * v, gsl_vector_short * w);
GSL_FUN int gsl_vector_short_swap_elements (gsl_vector_short * v, const size_t i, const size_t j);

GSL_FUN short gsl_vector_short_max (const gsl_vector_short * v);
GSL_FUN short gsl_vector_short_min (const gsl_vector_short * v);
GSL_FUN void gsl_vector_short_minmax (const gsl_vector_short * v, short * min_out, short * max_out);

GSL_FUN size_t gsl_vector_short_max_index (const gsl_vector_short * v);
GSL_FUN size_t gsl_vector_short_min_index (const gsl_vector_short * v);
GSL_FUN void gsl_vector_short_minmax_index (const gsl_vector_short * v, size_t * imin, size_t * imax);

GSL_FUN int gsl_vector_short_add (gsl_vector_short * a, const gsl_vector_short * b);
GSL_FUN int gsl_vector_short_sub (gsl_vector_short * a, const gsl_vector_short * b);
GSL_FUN int gsl_vector_short_mul (gsl_vector_short * a, const gsl_vector_short * b);
GSL_FUN int gsl_vector_short_div (gsl_vector_short * a, const gsl_vector_short * b);
GSL_FUN int gsl_vector_short_scale (gsl_vector_short * a, const double x);
GSL_FUN int gsl_vector_short_add_constant (gsl_vector_short * a, const double x);

GSL_FUN int gsl_vector_short_isnull (const gsl_vector_short * v);
GSL_FUN int gsl_vector_short_ispos (const gsl_vector_short * v);
GSL_FUN int gsl_vector_short_isneg (const gsl_vector_short * v);
GSL_FUN int gsl_vector_short_isnonneg (const gsl_vector_short * v);

GSL_FUN INLINE_DECL short gsl_vector_short_get (const gsl_vector_short * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_short_set (gsl_vector_short * v, const size_t i, short x);
GSL_FUN INLINE_DECL short * gsl_vector_short_ptr (gsl_vector_short * v, const size_t i);
GSL_FUN INLINE_DECL const short * gsl_vector_short_const_ptr (const gsl_vector_short * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
short
gsl_vector_short_get (const gsl_vector_short * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

INLINE_FUN
void
gsl_vector_short_set (gsl_vector_short * v, const size_t i, short x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

INLINE_FUN
short *
gsl_vector_short_ptr (gsl_vector_short * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (short *) (v->data + i * v->stride);
}

INLINE_FUN
const short *
gsl_vector_short_const_ptr (const gsl_vector_short * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const short *) (v->data + i * v->stride);
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_SHORT_H__ */


// Copyright (C) 2022. Huawei Technologies Co., Ltd. All rights reserved.

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef COMMON_H
#define COMMON_H

#include <cmath>
#include <optional>
#include <yato/array_view.h>

//#define CHECK_ASSERT

#include <training/system/Errors.h>
#include <training/system/Name.h>
#include <training/system/TypeHalf.h>
#include <training/system/Types.h>

#include <map>
#include <utility>

#if defined(_BLAS) && !defined(_BLAS_ENHANCE)
extern "C"
{
#include <cblas.h>
}
#else
#ifndef OPENBLAS_CONST
#define OPENBLAS_CONST const
#endif

typedef enum CBLAS_TRANSPOSE
{
    CblasNoTrans = 111,
    CblasTrans = 112,
    CblasConjTrans = 113,
    CblasConjNoTrans = 114
} CBLAS_TRANSPOSE;

typedef enum CBLAS_UPLO
{
    CblasUpper = 121,
    CblasLower = 122
} CBLAS_UPLO;
#endif

#ifdef CHECK_ASSERT
#define CHECK_NEAR ASSERT_NEAR
#else
#define CHECK_NEAR EXPECT_NEAR
#endif

#define RAUL_E 2.71828182845904523536        // e
#define RAUL_LOG2E 1.44269504088896340736    // log2(e)
#define RAUL_LOG10E 0.434294481903251827651  // log10(e)
#define RAUL_LN2 0.693147180559945309417     // ln(2)
#define RAUL_LN10 2.30258509299404568402     // ln(10)
#define RAUL_PI 3.14159265358979323846       // pi
#define RAUL_PI_2 1.57079632679489661923     // pi/2
#define RAUL_PI_4 0.785398163397448309616    // pi/4
#define RAUL_1_PI 0.318309886183790671538    // 1/pi
#define RAUL_2_PI 0.636619772367581343076    // 2/pi
#define RAUL_2_SQRTPI 1.12837916709551257390 // 2/sqrt(pi)
#define RAUL_SQRT2_PI 0.79788456080286535588 // sqrt(2/pi)
#define RAUL_SQRT2 1.41421356237309504880    // sqrt(2)
#define RAUL_SQRT1_2 0.707106781186547524401 // 1/sqrt(2)

#define GELU_CONST 0.044715

namespace raul
{

enum class Limit : int
{
    Left = 0,
    Middle = 1,
    Right = 2
};

enum class Dimension : int
{
    Default = -1,
    Batch = 0,
    Depth = 1,
    Height = 2,
    Width = 3
};

#if defined(_MSC_VER)
#define INLINE __forceinline
#else
#define INLINE __attribute__((always_inline))
#endif

template<typename Type>
class TensorImpl;
typedef TensorImpl<dtype> Tensor;
typedef TensorImpl<half> TensorFP16;

#if defined(ANDROID)
#define TOMMTYPE(var) static_cast<typename MM::type>(var)
#else
#define TOMMTYPE(var) castHelper<typename MM::type>::cast(var)
#endif

using shape = yato::dimensionality<4U, size_t>;
} // raul namespace

namespace raul
{

enum class NetworkMode
{
    Train = 0,
    Test = 1,
    TrainCheckpointed = 2
};

enum class CompressionMode
{
    NONE = -1,
    FP16 = 0,
    INT8 = 1
};

enum class CalculationMode
{
    DETERMINISTIC = 0,
#if defined(_OPENMP)
    FAST = 1,
#endif
};

/**
 * @brief Hardware target platform
 *
 */
enum class ExecutionTarget
{
    CPU = 0,
    CPUFP16 = 1
};

/**
 * @brief Hardware target platform per layer
 *
 * \note Might override execution target for workflow, useful for mixed precision
 */
enum class LayerExecutionTarget
{
    Default = -1, // use same as ExecutionTarget
    CPU = 0,      // from this point enums should be aligned with ExecutionTarget (due to LayerExecutionTarget = static_cast<ExecutionTarget>(enum))
    CPUFP16 = 1
};

/**
 * @brief Memory allocation mode
 */
enum class AllocationMode
{
    STANDARD,
    POOL
};

enum class DeclarationType
{
    Tensor = 0,
    Shape = 1,
    //    Alias = 2
};

class OpenclInitializer;

class Common
{
  public:

    // generate vector of random index permutation of [0..n-1]
    static void generate_permutation(size_t n, std::vector<size_t>& ind_vector, unsigned int seed = 0);

    /*
     * [cols x rows]
     * A[k x m]
     * B[n x k]
     * C[n x m]
     * https://software.intel.com/en-us/mkl-developer-reference-c-cblas-gemm
     * C = alpha * A * B + beta * C
     * bOffset - in elements (not bytes)
     */
    static void gemm(OPENBLAS_CONST CBLAS_TRANSPOSE transA,
                     OPENBLAS_CONST CBLAS_TRANSPOSE transB,
                     size_t m,
                     size_t n,
                     size_t k,
                     OPENBLAS_CONST dtype alpha,
                     OPENBLAS_CONST dtype* a,
                     OPENBLAS_CONST dtype* b,
                     OPENBLAS_CONST dtype beta,
                     dtype* c);

    static void gemm(OPENBLAS_CONST CBLAS_TRANSPOSE transA,
                     OPENBLAS_CONST CBLAS_TRANSPOSE transB,
                     size_t m,
                     size_t n,
                     size_t k,
                     OPENBLAS_CONST dtype alpha,
                     OPENBLAS_CONST half* a,
                     OPENBLAS_CONST half* b,
                     OPENBLAS_CONST dtype beta,
                     half* c);

    /**
     * @brief : Basic Linear Algebra Subroutine y = y + ax
     *
     *  \f[
     *      \vec{y} = \vec{y} + \alpha * \vec{x},
     *  \f]
     *
     * @param n The number of elements in vectors x and y.
     * @param sa The scalar alpha.
     * @param sx The vector x of length n. Specified as: a one-dimensional array of (at least) length \f$ 1+(n-1)|incx| \f$.
     * @param incx The stride for vector x. Specified as: an integer. It can have any value.
     * @param sy The vector y of length n. Specified as: a one-dimensional array of (at least) length \f$ 1+(n-1)|incy| \f$.
     * @param incy The stride for vector y.
     * @param xOffset The offset for vector x.
     * @param yOffset The offset for vector y.
     * @return The vector y, containing the results of the computation.
     */
    static void axpy(size_t n, OPENBLAS_CONST dtype sa, OPENBLAS_CONST dtype* sx, size_t incx, dtype* sy, size_t incy, size_t xOffset = 0, size_t yOffset = 0);
    static void axpy(size_t n, OPENBLAS_CONST dtype sa, OPENBLAS_CONST half* sx, size_t incx, half* sy, size_t incy, size_t xOffset = 0, size_t yOffset = 0);

    /**
     * @brief : Basic Linear Algebra Subroutine y = ax + by
     *
     *  \f[
     *      \vec{y} = \alpha \vec{x} + \beta \vec{y},
     *  \f]
     *
     * @param n The number of elements in vectors x and y.
     * @param alpha The scalar alpha.
     * @param x The vector x of length n. Specified as: a one-dimensional array of (at least) length \f$ 1+(n-1)|incx| \f$.
     * @param incx The stride for vector x. Specified as: an integer. It can have any value.
     * @param beta The scalar beta.
     * @param y The vector y of length n. Specified as: a one-dimensional array of (at least) length \f$ 1+(n-1)|incy| \f$.
     * @param incy The stride for vector y.
     * @param xOffset The offset for vector x.
     * @param yOffset The offset for vector y.
     * @return The vector y, containing the results of the computation.
     */
    static int axpby(OPENBLAS_CONST size_t n,
                     OPENBLAS_CONST dtype alpha,
                     OPENBLAS_CONST dtype* x,
                     OPENBLAS_CONST size_t incx,
                     OPENBLAS_CONST dtype beta,
                     dtype* y,
                     OPENBLAS_CONST size_t incy,
                     size_t xOffset,
                     size_t yOffset);

    static int axpby(OPENBLAS_CONST size_t n,
                     OPENBLAS_CONST dtype alpha,
                     OPENBLAS_CONST half* x,
                     OPENBLAS_CONST size_t incx,
                     OPENBLAS_CONST dtype beta,
                     half* y,
                     OPENBLAS_CONST size_t incy,
                     size_t xOffset,
                     size_t yOffset);
    /**
     * @brief : Basic Linear Algebra Subroutine y = alpha * a * x + beta * y
     *
     * Vector by vector element wise multiplication
     *
     *  \f[
     *      \vec{y} = \alpha \vec{a} \vec{x} + \beta \vec{y},
     *  \f]
     *
     * @param n The number of elements in vectors x and y.
     * @param alpha The scalar alpha.
     * @param a The vector of length n.
     * @param x The vector x of length n. Specified as: a one-dimensional array of (at least) length \f$ 1+(n-1)|incx| \f$.
     * @param incx The stride for vector x. Specified as: an integer. It can have any value.
     * @param beta The scalar beta.
     * @param y The vector y of length n. Specified as: a one-dimensional array of (at least) length \f$ 1+(n-1)|incy| \f$.
     * @param incy The stride for vector y.
     */

    static void hadamard(OPENBLAS_CONST size_t n,
                         OPENBLAS_CONST dtype alpha,
                         OPENBLAS_CONST dtype* a,
                         OPENBLAS_CONST dtype* x,
                         OPENBLAS_CONST size_t incx,
                         OPENBLAS_CONST dtype beta,
                         dtype* y,
                         OPENBLAS_CONST size_t incy);

    static dtype dot(size_t n, OPENBLAS_CONST dtype* sx, size_t incx, OPENBLAS_CONST dtype* sy, size_t incy);

    static void scal(size_t n, OPENBLAS_CONST dtype sa, dtype* sx, size_t incx);

    static void transpose(Tensor& tensor, size_t cols);
    static void transpose(TensorFP16& tensor, size_t cols);

    /*
     * memory for dst should be allocated externaly
     */
    static void addPadding1D(const dtype* src, dtype* dst, size_t srcChannels, size_t srcSize, size_t dstSize, bool reversedOrder = false);
    template<typename T>
    static void addPadding2D(const T* src, T* dst, size_t srcChannels, size_t srcWidth, size_t srcHeight, size_t dstWidth, size_t dstHeight)
    {
        if ((dstWidth >= srcWidth) && (dstHeight >= srcHeight))
        {
            size_t padWidth = dstWidth - srcWidth;
            size_t padHeight = dstHeight - srcHeight;

            size_t leftPad = padWidth / 2;
            // size_t rightPad = padWidth - leftPad;
            size_t topPad = padHeight / 2;
            size_t bottomPad = padHeight - topPad;

            for (size_t d = 0; d < srcChannels; ++d)
            {
                // top
                for (size_t y = 0; y < topPad; ++y)
                {
                    for (size_t x = 0; x < dstWidth; ++x)
                    {
                        dst[d * dstWidth * dstHeight + dstWidth * y + x] = static_cast<T>(0.0_dt);
                    }
                }

                for (size_t y = topPad; y < topPad + srcHeight; ++y)
                {
                    // left
                    for (size_t x = 0; x < leftPad; ++x)
                    {
                        dst[d * dstWidth * dstHeight + dstWidth * y + x] = static_cast<T>(0.0_dt);
                    }

                    // src
                    for (size_t x = leftPad; x < leftPad + srcWidth; ++x)
                    {
                        dst[d * dstWidth * dstHeight + dstWidth * y + x] = src[d * srcWidth * srcHeight + srcWidth * (y - topPad) + x - leftPad];
                    }

                    // right
                    for (size_t x = leftPad + srcWidth; x < dstWidth; ++x)
                    {
                        dst[d * dstWidth * dstHeight + dstWidth * y + x] = static_cast<T>(0.0_dt);
                    }
                }

                // bottom
                for (size_t y = dstHeight - bottomPad; y < dstHeight; ++y)
                {
                    for (size_t x = 0; x < dstWidth; ++x)
                    {
                        dst[d * dstWidth * dstHeight + dstWidth * y + x] = static_cast<T>(0.0_dt);
                    }
                }
            }
        }
    }

    /*
     * memory for dst should be allocated externaly
     */
    static void removePadding1D(const dtype* src, dtype* dst, size_t srcChannels, size_t srcSize, size_t dstSize, bool reversedOrder = false, bool overwrite = true);
    template<typename T>
    static void removePadding2D(const T* src, T* dst, size_t srcChannels, size_t srcWidth, size_t srcHeight, size_t dstWidth, size_t dstHeight, bool overwrite = true)
    {
        if ((dstWidth <= srcWidth) && (dstHeight <= srcHeight))
        {
            size_t padWidth = srcWidth - dstWidth;
            size_t padHeight = srcHeight - dstHeight;

            size_t leftPad = padWidth / 2;
            // size_t rightPad = padWidth - leftPad;
            size_t topPad = padHeight / 2;
            // size_t bottomPad = padHeight - topPad;

            if (overwrite)
            {
                for (size_t d = 0; d < srcChannels; ++d)
                {
                    for (size_t y = 0; y < dstHeight; ++y)
                    {
                        for (size_t x = 0; x < dstWidth; ++x)
                        {
                            dst[d * dstWidth * dstHeight + dstWidth * y + x] = src[d * srcWidth * srcHeight + srcWidth * (y + topPad) + x + leftPad];
                        }
                    }
                }
            }
            else
            {
                for (size_t d = 0; d < srcChannels; ++d)
                {
                    for (size_t y = 0; y < dstHeight; ++y)
                    {
                        for (size_t x = 0; x < dstWidth; ++x)
                        {
                            dst[d * dstWidth * dstHeight + dstWidth * y + x] += src[d * srcWidth * srcHeight + srcWidth * (y + topPad) + x + leftPad];
                        }
                    }
                }
            }
        }
    }

    /*
     * paddingWidth, paddingHeight - zero padding added for both sides of the input
     * memory for matrix should be allocated externaly
     */
    template<typename T>
    static void im2col(const T* image,
                       size_t imageWidth,
                       size_t imageHeight,
                       size_t imageChannels,
                       size_t filterWidth,
                       size_t filterHeight,
                       size_t strideWidth,
                       size_t strideHeight,
                       size_t paddingWidth,
                       size_t paddingHeight,
                       T* matrix,
                       bool reversedOrder = false);

    static size_t im2colOutputSize(size_t imageWidth,
                                   size_t imageHeight,
                                   size_t imageChannels,
                                   size_t filterWidth,
                                   size_t filterHeight,
                                   size_t strideWidth,
                                   size_t strideHeight,
                                   size_t paddingWidth,
                                   size_t paddingHeight,
                                   size_t dilationWidth,
                                   size_t dilationHeight);

    /*
     * paddingWidth, paddingHeight - zero padding added for both sides of the input
     * memory for image should be allocated externaly
     */
    template<typename T>
    static void col2im(const T* matrix,
                       size_t imageWidth,
                       size_t imageHeight,
                       size_t imageChannels,
                       size_t filterWidth,
                       size_t filterHeight,
                       size_t strideWidth,
                       size_t strideHeight,
                       size_t paddingWidth,
                       size_t paddingHeight,
                       T* image,
                       bool reversedOrder = false,
                       bool zeroOutput = true);

    /*
     * Rectified Linear Unit
     */
    template<typename T>
    static T ReLU(T x)
    {
        return std::max(static_cast<T>(0), x);
    }
    template<typename T>
    static T ReLU6(T x)
    {
        return std::min(std::max(static_cast<T>(0), x), static_cast<T>(6.0_dt));
    }

    template<typename T>
    static void ReLU(const T& in, T& out)
    {
        std::transform(in.begin(), in.end(), out.begin(), [&](typename T::type val) -> typename T::type { return ReLU(val); });
    }

    template<typename T>
    static void ReLU6(const T& in, T& out)
    {
        std::transform(in.begin(), in.end(), out.begin(), [&](typename T::type val) -> typename T::type { return ReLU6(val); });
    }

    template<typename T>
    static void ReLUBackward(const T& out, const T& delta, T& prevDelta)
    {
#if defined(_OPENMP)
#pragma omp parallel for
#endif
        for (size_t q = 0; q < prevDelta.size(); ++q)
        {
            prevDelta[q] += (out[q] > static_cast<typename T::type>(0)) ? delta[q] : static_cast<typename T::type>(0);
        }
    }

    template<typename T>
    static void ReLU6Backward(const T& out, const T& delta, T& prevDelta)
    {
#if defined(_OPENMP)
#pragma omp parallel for
#endif
        for (size_t q = 0; q < prevDelta.size(); ++q)
        {
            prevDelta[q] += (out[q] > static_cast<typename T::type>(0) && out[q] < static_cast<typename T::type>(6.0f)) ? delta[q] : static_cast<typename T::type>(0);
        }
    }

    /*
     * Gaussian error linear unit
     * @see https://arxiv.org/abs/1606.08415
     */
    static dtype GeLU_Erf(dtype x);
    static dtype GeLU_Tanh(dtype x);

    /*
     * Hard Sigmoid
     */
    template<typename T>
    static T HSigmoid(T x)
    {
        return static_cast<T>(ReLU6(TODTYPE(x) + 3.0_dt) / 6.0_dt);
    }

    /*
     * Hard Swish
     */
    template<typename T>
    static T HSwish(T x)
    {
        return x * HSigmoid(x);
    }
    static dtype sign(dtype x) { return TODTYPE((0.0_dt < x) - (x < 0.0_dt)); }

    template<typename T, typename U>
    static void copyView(const T& view_from, U& view_to, const bool overwrite = false)
    {
        auto retLhs = [](typename T::value_type& lhs, [[maybe_unused]] typename T::value_type& rhs) { return lhs; };

        auto copyViewImpl = [](const T& view_from, U& view_to, auto&& func)
        {
            for (size_t i1 = 0; i1 < view_from.size(0); ++i1)
            {
                for (size_t i2 = 0; i2 < view_from.size(1); ++i2)
                {
                    for (size_t i3 = 0; i3 < view_from.size(2); ++i3)
                    {
                        for (size_t i4 = 0; i4 < view_from.size(3); ++i4)
                        {
                            view_to[i1][i2][i3][i4] = func(view_from[i1][i2][i3][i4], view_to[i1][i2][i3][i4]);
                        }
                    }
                }
            }
        };

        if (overwrite)
        {
            copyViewImpl(view_from, view_to, retLhs);
        }
        else
        {
            copyViewImpl(view_from, view_to, std::plus<typename T::value_type>());
        }
    }

    template<typename T>
    static void unpack4D(const T& src, T& dst, Dimension dir, size_t index, const Name& layerType, const Name& layerName, bool overwrite)
    {
        auto input4d = src.get4DView();
        auto inputDims = yato::dims(src.getDepth(), src.getHeight(), src.getWidth());

        auto outputDims = dst.getShape();

        const typename T::type* startEl = nullptr;
        switch (dir)
        {
            case Dimension::Depth:
                startEl = &input4d[0][index][0][0];
                break;
            case Dimension::Height:
                startEl = &input4d[0][0][index][0];
                break;
            default:
                throw std::runtime_error(layerType + "[" + layerName + "]: unpack4D unknown dim");
        }

        auto srcView = yato::array_view_4d<const typename T::type>(startEl, outputDims, inputDims);
        auto outputView = dst.get4DView();
        Common::copyView(srcView, outputView, overwrite);
    }

    template<typename T>
    static void pack4D(const T& src, T& dst, Dimension dir, size_t index, const Name& layerType, const Name& layerName, bool overwrite)
    {
        auto output4d = dst.get4DView();

        yato::dimensionality<3U, size_t> concatDims(dst.getDepth(), dst.getHeight(), dst.getWidth());

        auto srcView = src.get4DView();
        typename T::type* startEl = nullptr;
        switch (dir)
        {
            case Dimension::Depth:
                startEl = &output4d[0][index][0][0];
                break;
            case Dimension::Height:
                startEl = &output4d[0][0][index][0];
                break;
            default:
                throw std::runtime_error(layerType + "[" + layerName + "]: pack4D unknown dim");
        }

        auto dstView = yato::array_view_4d<typename T::type>(startEl, src.getShape(), concatDims);
        Common::copyView(srcView, dstView, overwrite);
    }

    /*
     * Upper triangle of a rectangular array
     */
    template<typename T>
    static void triu(T* data, size_t nrows, size_t ncols, int diag = 0)
    {
        size_t i = 0;
        int cols = (int)ncols;
        int rows = (int)nrows;
        for (int r = 0; r < rows; ++r)
        {
            for (int c = 0; c < cols; ++c, ++i)
            {
                if (c - r - diag < 0)
                {
                    data[i] = static_cast<T>(0);
                }
            }
        }
    }

    /*
     * Applies a 1D convolution over an input signal composed of several input planes.
     * Supports 2 modes:
     *  1. PyTorch style: Input[N, C, 1, L1] (or [N, 1, C, L1]) -> Output[N, FILTERS, 1, L2] (or [N, 1, FILTERS, L2])
     *  2. TensorFlow style: Input[N, L1, 1, C] (or [N, 1, L1, C]) -> Output[N, L2, 1, FILTERS] (or [N, 1, L2, FILTERS])
     * Output is not zeroed prior to convolution (operator += is used)
     */
    static void conv1d(const dtype* input,
                       dtype* output,
                       const dtype* kernel,
                       const dtype* bias,
                       size_t batchSize,
                       size_t inputSize,
                       size_t inputChannels,
                       size_t outputSize,
                       size_t outputChannels,
                       size_t kernelSize,
                       size_t padding,
                       size_t stride,
                       size_t dilation = 1U,
                       size_t groups = 1U,
                       bool tfStyle = false);

    /*
     * Applies 2D convolution over input tensor, all channels convolved
     * Output is not zeroed prior to convolution (operator += is used)
     */
    template<typename T>
    static void conv2d(const T* input,
                       T* output,
                       const T* kernel,
                       const T* bias,
                       size_t batchSize,
                       size_t inputWidth,
                       size_t inputHeight,
                       size_t inputChannels,
                       size_t outputWidth,
                       size_t outputHeight,
                       size_t outputChannels,
                       size_t kernelWidth,
                       size_t kernelHeight,
                       size_t paddingW,
                       size_t paddingH,
                       size_t strideW,
                       size_t strideH,
                       size_t dilationW = 1U,
                       size_t dilationH = 1U,
                       size_t groups = 1U)
    {
        auto inputs3D = yato::array_view_3d<T>(const_cast<T*>(input), yato::dims(batchSize, inputChannels, inputHeight * inputWidth));
        auto outputs3D = yato::array_view_3d<T>(output, yato::dims(batchSize, outputChannels, outputHeight * outputWidth));
        auto kernelsWeights4D = yato::array_view_4d<T>(const_cast<T*>(kernel), yato::dims(outputChannels, inputChannels / groups, kernelHeight, kernelWidth));

        for (size_t q = 0; q < batchSize; ++q)
        {
            for (size_t d = 0; d < outputChannels; ++d)
            {
                std::fill(outputs3D[q][d].begin(), outputs3D[q][d].end(), static_cast<T>(0.0_dt));
            }

            size_t inputWidthPadded = inputWidth + 2 * paddingW;
            size_t inputHeightPadded = inputHeight + 2 * paddingH;

            std::vector<T> inputPadded(inputChannels * inputHeightPadded * inputWidthPadded);

            Common::addPadding2D(&inputs3D[q][0][0], inputPadded.data(), inputChannels, inputWidth, inputHeight, inputWidthPadded, inputHeightPadded);

            auto inputPadded2D = yato::view(inputPadded).reshape(yato::dims(inputChannels, inputHeightPadded * inputWidthPadded));

            for (size_t group = 0; group < groups; ++group)
            {
                for (size_t kernelIndex = 0; kernelIndex < outputChannels / groups; ++kernelIndex)
                {
                    for (size_t d = 0; d < inputChannels / groups; ++d)
                    {
                        for (size_t oy = 0; oy < outputHeight; ++oy)
                        {
                            for (size_t ox = 0; ox < outputWidth; ++ox)
                            {
                                for (size_t ky = 0; ky < kernelHeight; ++ky)
                                {
                                    for (size_t kx = 0; kx < kernelWidth; ++kx)
                                    {
                                        outputs3D[q][kernelIndex + group * outputChannels / groups][oy * outputWidth + ox] +=
                                            kernelsWeights4D[kernelIndex + group * outputChannels / groups][d][ky][kx] *
                                            inputPadded2D[d + group * inputChannels / groups][oy * inputWidthPadded * strideH + ky * dilationH * inputWidthPadded + ox * strideW + kx * dilationW];
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        if (bias)
        {
            for (size_t q = 0; q < batchSize; ++q)
            {
                for (size_t kernelIndex = 0; kernelIndex < outputChannels; ++kernelIndex)
                {
                    for (size_t oy = 0; oy < outputHeight; ++oy)
                    {
                        for (size_t ox = 0; ox < outputWidth; ++ox)
                        {
                            outputs3D[q][kernelIndex][oy * outputWidth + ox] += bias[kernelIndex];
                        }
                    }
                }
            }
        }
    }

    template<typename T = dtype, typename Iterator>
    static void arange(Iterator begin, Iterator end, T start = static_cast<T>(0), T step = static_cast<T>(1))
    {
        auto val = start;
        for (auto p = begin; p != end; ++p)
        {
            *p = static_cast<std::remove_reference_t<decltype(*p)>>(val);
            val += step;
        }
    }

    template<typename T = dtype, typename Iterable>
    static void arange(Iterable& i, T start = static_cast<T>(0), T step = static_cast<T>(1))
    {
        return arange(i.begin(), i.end(), start, step);
    }

    static void replaceAll(std::string& str, const std::string& srcSubstr, const std::string& tgtSubstr)
    {
        size_t start_pos = 0;
        while ((start_pos = str.find(srcSubstr, start_pos)) != std::string::npos)
        {
            str.replace(start_pos, srcSubstr.length(), tgtSubstr);
            start_pos += tgtSubstr.length(); // srcSubstr could be a substring of tgtSubstr
        }
    }

    static bool startsWith(const std::string& str, const std::string& srcSubstr) { return (str.rfind(srcSubstr, 0) == 0); }

    static std::vector<std::string> split(const std::string& string, char delimeter);

    /*
     * @see https://docs.scipy.org/doc/numpy-1.13.0/user/basics.broadcasting.html
     */
    template<typename T>
    static bool shapeIsBroadcastable(const T& from, const T& to)
    {
        const auto n = to.dimensions_num();
        for (size_t i = 0; i < n; ++i)
        {
            if (from[i] != to[i] && from[i] != 1U && to[i] != 1U)
            {
                return false;
            }
        }
        return true;
    }

    static bool endsWith(std::string const& value, std::string const& ending)
    {
        if (ending.size() > value.size())
        {
            return false;
        }
        return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
    }

    static shape getStrides(const shape& tensor_shape);

    static shape offsetToIndexes(size_t offset, const shape& strides);

    static size_t indexesToOffset(const shape& indexes, const shape& strides);
};

template<class T>
bool if_equals(const std::string&& error, const T val1, const T val2)
{
    if (val1 != val2)
    {
        throw(std::runtime_error(error));
    }
    return val1 == val2;
}

} // raul namespace

#endif // COMMON_H


// --------------------------------------------------------------------------
//                   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: Stephan Aiche$
// $Authors: Stephan Aiche, Marc Sturm $
// --------------------------------------------------------------------------

#ifndef OPENMS_TRANSFORMATIONS_FEATUREFINDER_TRACEFITTER_H
#define OPENMS_TRANSFORMATIONS_FEATUREFINDER_TRACEFITTER_H

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

#include <OpenMS/DATASTRUCTURES/DefaultParamHandler.h>
#include <OpenMS/DATASTRUCTURES/ListUtils.h>

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

namespace OpenMS
{

  /**
   * @brief Abstract fitter for RT profile fitting
   *
   * This class provides the basic interface and some functionality to fit multiple mass traces to
   * a given RT shape model using the Levenberg-Marquardt algorithm.
   *
   * @todo docu needs update
   *
   */
  template <class PeakType>
  class TraceFitter :
    public DefaultParamHandler
  {

public:
    /// default constructor.
    TraceFitter() :
      DefaultParamHandler("TraceFitter")
    {
      defaults_.setValue("max_iteration", 500, "Maximum number of iterations used by the Levenberg-Marquardt algorithm.", ListUtils::create<String>("advanced"));
      defaults_.setValue("epsilon_abs", 0.0001, "Absolute error used by the Levenberg-Marquardt algorithm.", ListUtils::create<String>("advanced"));
      defaults_.setValue("epsilon_rel", 0.0001, "Relative error used by the Levenberg-Marquardt algorithm.", ListUtils::create<String>("advanced"));
      defaults_.setValue("weighted", "false", "Weight mass traces according to their theoretical intensities.", ListUtils::create<String>("advanced"));
      defaults_.setValidStrings("weighted", ListUtils::create<String>("true,false"));
      defaultsToParam_();
    }

    /// copy constructor
    TraceFitter(const TraceFitter& source) :
      DefaultParamHandler(source),
      epsilon_abs_(source.epsilon_abs_),
      epsilon_rel_(source.epsilon_rel_),
      max_iterations_(source.max_iterations_),
      weighted_(source.weighted_)
    {
      updateMembers_();
    }

    /// assignment operator
    virtual TraceFitter& operator=(const TraceFitter& source)
    {
      DefaultParamHandler::operator=(source);
      max_iterations_ = source.max_iterations_;
      epsilon_abs_  = source.epsilon_abs_;
      epsilon_rel_ = source.epsilon_rel_;
      weighted_ = source.weighted_;
      updateMembers_();

      return *this;
    }

    /// destructor
    virtual ~TraceFitter()
    {
    }

    /**
     * Main method of the TraceFitter which triggers the actual fitting.
     */
    virtual void fit(FeatureFinderAlgorithmPickedHelperStructs::MassTraces<PeakType>& traces) = 0;

    /**
     * Returns the lower bound of the fitted RT model
     */
    virtual DoubleReal getLowerRTBound() const = 0;

    /**
     * Returns the upper bound of the fitted RT model
     */
    virtual DoubleReal getUpperRTBound() const = 0;

    /**
     * Returns the height of the fitted model
     */
    virtual DoubleReal getHeight() const = 0;

    /**
     * Returns the center position of the fitted model
     */
    virtual DoubleReal getCenter() const = 0;

    /**
     * Returns the mass trace width at half max (FWHM)
     */
    virtual DoubleReal getFWHM() const = 0;

    /**
     * Evaluate the fitted model at a time point
     */
    virtual DoubleReal getValue(DoubleReal rt) const = 0;

    /**
     * Returns the theoretical value of the fitted model at position k in the passed mass trace
     *
     * @param trace the mass trace for which the value should be computed
     * @param k  use the position of the k-th peak to compute the value
     */
    DoubleReal computeTheoretical(const FeatureFinderAlgorithmPickedHelperStructs::MassTrace<PeakType>& trace, Size k)
    {
      double rt = trace.peaks[k].first;

      return trace.theoretical_int * getValue(rt);
    }

    /**
     * Checks if the fitted model fills out at least 'min_rt_span' of the RT span
     *
     * @param rt_bounds RT boundaries of the fitted model
     * @param min_rt_span Minimum RT span in relation to extended area that has to remain after model fitting
     */
    virtual bool checkMinimalRTSpan(const std::pair<DoubleReal, DoubleReal>& rt_bounds, const DoubleReal min_rt_span) = 0;

    /**
     * Checks if the fitted model is not to big
     *
     * @param max_rt_span Maximum RT span in relation to extended area that the model is allowed to have
     */
    virtual bool checkMaximalRTSpan(const DoubleReal max_rt_span) = 0;

    /**
     * Returns the peak area of the fitted model
     */
    virtual DoubleReal getArea() = 0;

    /**
     * Returns a textual representation of the fitted model function, that can be plotted using Gnuplot
     *
     * @param trace The mass trace that should be plotted
     * @param function_name The name of the function (e.g. f(x) -> function_name = f)
     * @param baseline The intensity of the baseline
     * @param rt_shift A shift value, that allows to plot all RT profiles side by side, even if they would overlap in reality.
     *                 This should be 0 for the first mass trace and increase by a fixed value for each mass trace.
     */
    virtual String getGnuplotFormula(const FeatureFinderAlgorithmPickedHelperStructs::MassTrace<PeakType>& trace, const char function_name, const DoubleReal baseline, const DoubleReal rt_shift) = 0;

protected:

    /// Structure for passing data to GSL functions
    struct ModelData
    {
      FeatureFinderAlgorithmPickedHelperStructs::MassTraces<PeakType>* traces_ptr;
      bool weighted;
    };

    /**
     * Prints the state of the current iteration (e.g., values of the parameters)
     *
     * @param iter Number of current iteration.
     * @param s The solver that also contains all the parameters.
     */
    virtual void printState_(SignedSize iter, gsl_multifit_fdfsolver* s) = 0;

    virtual void updateMembers_()
    {
      max_iterations_ = this->param_.getValue("max_iteration");
      epsilon_abs_ = this->param_.getValue("epsilon_abs");
      epsilon_rel_ = this->param_.getValue("epsilon_rel");
      weighted_ = this->param_.getValue("weighted") == "true";
    }

    /**
     * Updates all member variables to the fitted values stored in the solver.
     *
     * @param s The solver containing the fitted parameter values.
     */
    virtual void getOptimizedParameters_(gsl_multifit_fdfsolver* s) = 0;

    /**
     * Optimize the given parameters using the Levenberg-Marquardt algorithm.
     */
    void optimize_(FeatureFinderAlgorithmPickedHelperStructs::MassTraces<PeakType> & traces, const Size num_params, double 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))
    {
      const gsl_multifit_fdfsolver_type* T;
      gsl_multifit_fdfsolver* s;

      const size_t data_count = traces.getPeakCount();

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

      gsl_multifit_function_fdf func;
      gsl_vector_view x = gsl_vector_view_array(x_init, num_params);
      gsl_rng_env_setup();
      func.f = (residual);
      func.df = (jacobian);
      func.fdf = (evaluate);
      func.n = data_count;
      func.p = num_params;
      ModelData params = {&traces, weighted_};
      func.params = &params;
      T = gsl_multifit_fdfsolver_lmsder;
      s = gsl_multifit_fdfsolver_alloc(T, data_count, num_params);
      gsl_multifit_fdfsolver_set(s, &func, &x.vector);
      SignedSize iter = 0;
      Int gsl_status_;
      do
      {
        iter++;
        gsl_status_ = gsl_multifit_fdfsolver_iterate(s);
        printState_(iter, s);
        if (gsl_status_) break;
        gsl_status_ = gsl_multifit_test_delta(s->dx, s->x, epsilon_abs_, epsilon_rel_);
      }
      while (gsl_status_ == GSL_CONTINUE && iter < max_iterations_);

      // get the parameters out of the fdfsolver
      getOptimizedParameters_(s);

      gsl_multifit_fdfsolver_free(s);
    }

    /** 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
    DoubleReal epsilon_abs_;
    /// Relative error
    DoubleReal epsilon_rel_;
    /// Maximum number of iterations
    SignedSize max_iterations_;
    /// Whether to weight mass traces by theoretical intensity during the optimization
    bool weighted_;

  };

}

#endif // #ifndef OPENMS_TRANSFORMATIONS_FEATUREFINDER_FEATUREFINDERALGORITHMPICKED_RTFITTING_H


/**
 * This file is part of yasimSBML (http://www.labri.fr/perso/ghozlane/metaboflux/about/yasimSBML.php)
 * Copyright (C) 2010 Amine Ghozlane from LaBRI and University of Bordeaux 1
 *
 * yasimSBML is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * yasimSBML is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the Lesser GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * \file yasimSBML.c
 * \brief Main program
 * \author {Amine Ghozlane}
 * \version 1.0
 * \date 27 octobre 2009
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <gsl/gsl_rng.h>
#include <sbml/SBMLTypes.h>
#include "especes.h"
#include "simulation.h"

/**
 * \fn int main(int argc, char **argv)
 * \author Amine Ghozlane
 * \brief  Enter in the program for simulated annealing
 * \param  argc Number of arguments
 * \param  argv List of arguments
 * \return EXIT_SUCCESS Normal stop of the program
 */
int main(int argc, char **argv) {
  SBMLDocument_t *file=NULL;
  Model_t *mod=NULL;
  unsigned int errors = 0;
  /*unsigned int level;
  unsigned int version;*/

  /* File adress */
  if (argc <= 2) {
      fprintf(stderr, "Usage ./yasimSBML.exe sbml_file.xml number_of_simulation\n");
      return 1;
  }

  /* Load File  */
  file = readSBML(argv[1]);
  errors = SBMLDocument_getNumErrors(file);

  /* Error File */
  if (errors > 0) {
      printf("Encountered the following SBML error(s):\n");
      SBMLDocument_printErrors(file, stdout);
      printf("Simulation skipped.  Please correct the problems above first.\n");
      return errors;
  }

  /*level = SBMLDocument_getLevel(file);
  version = SBMLDocument_getVersion(file);
  printf("SBML Level : %d, version : %d\n", level, version);*/

  /* Testing mod file */
  mod = SBMLDocument_getModel(file);
  if (mod == NULL)
    fprintf(stderr, "Error: file %s doesn't exist!\n", argv[1]);
  /*compute_simulation(mod);*/
  SBML_compute_simulation_mean(mod,atoi(argv[2]));

  /* free memory */
  Model_free(mod);
  /*SBMLDocument_free(file);*/

  return EXIT_SUCCESS;
}


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

#ifndef __GSL_MATRIX_LONG_DOUBLE_H__
#define __GSL_MATRIX_LONG_DOUBLE_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_long_double.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  long double * data;
  gsl_block_long_double * block;
  int owner;
} gsl_matrix_long_double;

typedef struct
{
  gsl_matrix_long_double matrix;
} _gsl_matrix_long_double_view;

typedef _gsl_matrix_long_double_view gsl_matrix_long_double_view;

typedef struct
{
  gsl_matrix_long_double matrix;
} _gsl_matrix_long_double_const_view;

typedef const _gsl_matrix_long_double_const_view gsl_matrix_long_double_const_view;

/* Allocation */

GSL_FUN gsl_matrix_long_double *
gsl_matrix_long_double_alloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_long_double *
gsl_matrix_long_double_calloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_long_double *
gsl_matrix_long_double_alloc_from_block (gsl_block_long_double * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_FUN gsl_matrix_long_double *
gsl_matrix_long_double_alloc_from_matrix (gsl_matrix_long_double * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN gsl_vector_long_double *
gsl_vector_long_double_alloc_row_from_matrix (gsl_matrix_long_double * m,
                                        const size_t i);

GSL_FUN gsl_vector_long_double *
gsl_vector_long_double_alloc_col_from_matrix (gsl_matrix_long_double * m,
                                        const size_t j);

GSL_FUN void gsl_matrix_long_double_free (gsl_matrix_long_double * m);

/* Views */

GSL_FUN _gsl_matrix_long_double_view
gsl_matrix_long_double_submatrix (gsl_matrix_long_double * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_row (gsl_matrix_long_double * m, const size_t i);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_column (gsl_matrix_long_double * m, const size_t j);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_diagonal (gsl_matrix_long_double * m);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_subdiagonal (gsl_matrix_long_double * m, const size_t k);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_superdiagonal (gsl_matrix_long_double * m, const size_t k);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_subrow (gsl_matrix_long_double * m, const size_t i,
                         const size_t offset, const size_t n);

GSL_FUN _gsl_vector_long_double_view
gsl_matrix_long_double_subcolumn (gsl_matrix_long_double * m, const size_t j,
                            const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_long_double_view
gsl_matrix_long_double_view_array (long double * base,
                             const size_t n1,
                             const size_t n2);

GSL_FUN _gsl_matrix_long_double_view
gsl_matrix_long_double_view_array_with_tda (long double * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_FUN _gsl_matrix_long_double_view
gsl_matrix_long_double_view_vector (gsl_vector_long_double * v,
                              const size_t n1,
                              const size_t n2);

GSL_FUN _gsl_matrix_long_double_view
gsl_matrix_long_double_view_vector_with_tda (gsl_vector_long_double * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_FUN _gsl_matrix_long_double_const_view
gsl_matrix_long_double_const_submatrix (const gsl_matrix_long_double * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_row (const gsl_matrix_long_double * m,
                            const size_t i);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_column (const gsl_matrix_long_double * m,
                               const size_t j);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_diagonal (const gsl_matrix_long_double * m);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_subdiagonal (const gsl_matrix_long_double * m,
                                    const size_t k);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_superdiagonal (const gsl_matrix_long_double * m,
                                      const size_t k);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_subrow (const gsl_matrix_long_double * m, const size_t i,
                               const size_t offset, const size_t n);

GSL_FUN _gsl_vector_long_double_const_view
gsl_matrix_long_double_const_subcolumn (const gsl_matrix_long_double * m, const size_t j,
                                  const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_long_double_const_view
gsl_matrix_long_double_const_view_array (const long double * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_FUN _gsl_matrix_long_double_const_view
gsl_matrix_long_double_const_view_array_with_tda (const long double * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_FUN _gsl_matrix_long_double_const_view
gsl_matrix_long_double_const_view_vector (const gsl_vector_long_double * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN _gsl_matrix_long_double_const_view
gsl_matrix_long_double_const_view_vector_with_tda (const gsl_vector_long_double * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_FUN void gsl_matrix_long_double_set_zero (gsl_matrix_long_double * m);
GSL_FUN void gsl_matrix_long_double_set_identity (gsl_matrix_long_double * m);
GSL_FUN void gsl_matrix_long_double_set_all (gsl_matrix_long_double * m, long double x);

GSL_FUN int gsl_matrix_long_double_fread (FILE * stream, gsl_matrix_long_double * m) ;
GSL_FUN int gsl_matrix_long_double_fwrite (FILE * stream, const gsl_matrix_long_double * m) ;
GSL_FUN int gsl_matrix_long_double_fscanf (FILE * stream, gsl_matrix_long_double * m);
GSL_FUN int gsl_matrix_long_double_fprintf (FILE * stream, const gsl_matrix_long_double * m, const char * format);

GSL_FUN int gsl_matrix_long_double_memcpy(gsl_matrix_long_double * dest, const gsl_matrix_long_double * src);
GSL_FUN int gsl_matrix_long_double_swap(gsl_matrix_long_double * m1, gsl_matrix_long_double * m2);

GSL_FUN int gsl_matrix_long_double_swap_rows(gsl_matrix_long_double * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_long_double_swap_columns(gsl_matrix_long_double * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_long_double_swap_rowcol(gsl_matrix_long_double * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_long_double_transpose (gsl_matrix_long_double * m);
GSL_FUN int gsl_matrix_long_double_transpose_memcpy (gsl_matrix_long_double * dest, const gsl_matrix_long_double * src);

GSL_FUN long double gsl_matrix_long_double_max (const gsl_matrix_long_double * m);
GSL_FUN long double gsl_matrix_long_double_min (const gsl_matrix_long_double * m);
GSL_FUN void gsl_matrix_long_double_minmax (const gsl_matrix_long_double * m, long double * min_out, long double * max_out);

GSL_FUN void gsl_matrix_long_double_max_index (const gsl_matrix_long_double * m, size_t * imax, size_t *jmax);
GSL_FUN void gsl_matrix_long_double_min_index (const gsl_matrix_long_double * m, size_t * imin, size_t *jmin);
GSL_FUN void gsl_matrix_long_double_minmax_index (const gsl_matrix_long_double * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_FUN int gsl_matrix_long_double_isnull (const gsl_matrix_long_double * m);
GSL_FUN int gsl_matrix_long_double_ispos (const gsl_matrix_long_double * m);
GSL_FUN int gsl_matrix_long_double_isneg (const gsl_matrix_long_double * m);
GSL_FUN int gsl_matrix_long_double_isnonneg (const gsl_matrix_long_double * m);

GSL_FUN int gsl_matrix_long_double_add (gsl_matrix_long_double * a, const gsl_matrix_long_double * b);
GSL_FUN int gsl_matrix_long_double_sub (gsl_matrix_long_double * a, const gsl_matrix_long_double * b);
GSL_FUN int gsl_matrix_long_double_mul_elements (gsl_matrix_long_double * a, const gsl_matrix_long_double * b);
GSL_FUN int gsl_matrix_long_double_div_elements (gsl_matrix_long_double * a, const gsl_matrix_long_double * b);
GSL_FUN int gsl_matrix_long_double_scale (gsl_matrix_long_double * a, const double x);
GSL_FUN int gsl_matrix_long_double_add_constant (gsl_matrix_long_double * a, const double x);
GSL_FUN int gsl_matrix_long_double_add_diagonal (gsl_matrix_long_double * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_FUN int gsl_matrix_long_double_get_row(gsl_vector_long_double * v, const gsl_matrix_long_double * m, const size_t i);
GSL_FUN int gsl_matrix_long_double_get_col(gsl_vector_long_double * v, const gsl_matrix_long_double * m, const size_t j);
GSL_FUN int gsl_matrix_long_double_set_row(gsl_matrix_long_double * m, const size_t i, const gsl_vector_long_double * v);
GSL_FUN int gsl_matrix_long_double_set_col(gsl_matrix_long_double * m, const size_t j, const gsl_vector_long_double * v);
/***********************************************************************/

/* inline functions if you are using GCC */

GSL_FUN INLINE_DECL long double   gsl_matrix_long_double_get(const gsl_matrix_long_double * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL void    gsl_matrix_long_double_set(gsl_matrix_long_double * m, const size_t i, const size_t j, const long double x);
GSL_FUN INLINE_DECL long double * gsl_matrix_long_double_ptr(gsl_matrix_long_double * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL const long double * gsl_matrix_long_double_const_ptr(const gsl_matrix_long_double * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE
INLINE_FUN
long double
gsl_matrix_long_double_get(const gsl_matrix_long_double * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
        }
    }
#endif
  return m->data[i * m->tda + j] ;
}

INLINE_FUN
void
gsl_matrix_long_double_set(gsl_matrix_long_double * m, const size_t i, const size_t j, const long double x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  m->data[i * m->tda + j] = x ;
}

INLINE_FUN
long double *
gsl_matrix_long_double_ptr(gsl_matrix_long_double * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (long double *) (m->data + (i * m->tda + j)) ;
}

INLINE_FUN
const long double *
gsl_matrix_long_double_const_ptr(const gsl_matrix_long_double * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (const long double *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_LONG_DOUBLE_H__ */


#include <gsl/gsl_errno.h>
#include <gsl/matrix/gsl_matrix.h>
#include <string.h>

#define BASE_DOUBLE
#include <gsl/templates_on.h>
#include <gsl/matrix/copy_source.c>
#include <gsl/templates_off.h>
#undef BASE_DOUBLE


#pragma once

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

namespace Library
{
	class ProxyModel;
}

namespace Rendering
{
	class SpotLightMaterial;

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

		float AmbientLightIntensity() const;
		void SetAmbientLightIntensity(float intensity);

		float SpotLightIntensity() const;
		void SetSpotLightIntensity(float intensity);

		const DirectX::XMFLOAT3& LightPosition() const;
		const DirectX::XMVECTOR LightPositionVector() const;
		void SetLightPosition(const DirectX::XMFLOAT3& position);
		void SetLightPosition(DirectX::FXMVECTOR position);

		const DirectX::XMFLOAT3& LightLookAt() const;
		void RotateSpotLight(const DirectX::XMFLOAT2& amount);

		float LightRadius() const;
		void SetLightRadius(float radius);

		float SpecularIntensity() const;
		void SetSpecularIntensity(float intensity);

		float SpecularPower() const;
		void SetSpecularPower(float power);

		float SpotLightInnerAngle() const;
		void SetSpotLightInnerAngle(float angle);

		float SpotLightOuterAngle() const;
		void SetSpotLightOuterAngle(float angle);

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

	private:
		std::shared_ptr<SpotLightMaterial> mMaterial;
		DirectX::XMFLOAT4X4 mWorldMatrix{ Library::MatrixHelper::Identity };
		winrt::com_ptr<ID3D11Buffer> mVertexBuffer;
		std::uint32_t mVertexCount{ 0 };
		Library::SpotLight mSpotLight;
		std::unique_ptr<Library::ProxyModel> mProxyModel;
		float mModelRotationAngle{ 0.0f };
		bool mUpdateMaterial{ true };
	};
}


#pragma once

#include <gsl/gsl>
#include <memory>

struct SpeexResamplerState_;
typedef struct SpeexResamplerState_ SpeexResamplerState;

namespace Halley
{
	struct AudioResamplerResult
	{
		size_t nRead;
		size_t nWritten;
	};

	class AudioResampler
	{
	public:
		AudioResampler(int from, int to, int nChannels, float quality = 1.0f);
		~AudioResampler();

		AudioResamplerResult resample(gsl::span<const float> src, gsl::span<float> dst, size_t channel);
		AudioResamplerResult resampleInterleaved(gsl::span<const float> src, gsl::span<float> dst);
		AudioResamplerResult resampleInterleaved(gsl::span<const short> src, gsl::span<short> dst);
		AudioResamplerResult resampleNoninterleaved(gsl::span<const float> src, gsl::span<float> dst, const size_t numChannels);
		size_t numOutputSamples(size_t numInputSamples) const;

	private:
		std::unique_ptr<SpeexResamplerState, void(*)(SpeexResamplerState*)> resampler;
		size_t nChannels;
		int from;
		int to;
	};
}