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

#pragma once

#include <cblas.h>
#include <cuda_runtime_api.h>

#include <boost/shared_ptr.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "modules/perception/base/blob.h"
#include "modules/perception/base/image.h"

namespace apollo {
namespace perception {
namespace inference {

bool ResizeGPU(const base::Image8U &src,
               std::shared_ptr<apollo::perception::base::Blob<float>> dst,
               int stepwidth, int start_axis);

bool ResizeGPU(const apollo::perception::base::Blob<uint8_t> &src_gpu,
               std::shared_ptr<apollo::perception::base::Blob<float>> dst,
               int stepwidth, int start_axis, int mean_b, int mean_g,
               int mean_r, bool channel_axis, float scale);

bool ResizeGPU(const base::Image8U &src,
               std::shared_ptr<apollo::perception::base::Blob<float>> dst,
               int stepwidth, int start_axis, float mean_b, float mean_g,
               float mean_r, bool channel_axis, float scale);

}  // namespace inference
}  // namespace perception
}  // namespace apollo


#ifndef SOLVER_H
#define SOLVER_H

#if PY_MAJOR_VERSION >= 3
#define PY3K
#endif

// include user-defined headers
#include "common.h"
#include "utils.h"

#include <gsl/gsl_errno.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_spline.h>

typedef struct _TOV_Tuple {
    double a;
    double b;
    double c;
} Pair;

#endif


#pragma once

#include <gsl\gsl>
#include <d3d11.h>
#include "Bone.h"

namespace Library
{
	class Model;
    class Material;
    class ModelMaterial;
	class OutputStreamHelper;
	class InputStreamHelper;

	struct MeshData final
	{
		std::shared_ptr<ModelMaterial> Material;
		std::string Name;
		std::vector<DirectX::XMFLOAT3> Vertices;
		std::vector<DirectX::XMFLOAT3> Normals;
		std::vector<DirectX::XMFLOAT3> Tangents;
		std::vector<DirectX::XMFLOAT3> BiNormals;
		std::vector<std::vector<DirectX::XMFLOAT3>> TextureCoordinates;
		std::vector<std::vector<DirectX::XMFLOAT4>> VertexColors;
		std::uint32_t FaceCount{ 0 };
		std::vector<std::uint32_t> Indices;
		std::vector<BoneVertexWeights> BoneWeights;
	};

    class Mesh final
    {
    public:
		Mesh(Library::Model& model, InputStreamHelper& streamHelper);
		Mesh(Library::Model& model, MeshData&& meshData);
		Mesh(const Mesh&) = default;
		Mesh(Mesh&&) = default;
		Mesh& operator=(const Mesh&) = default;
		Mesh& operator=(Mesh&&) = default;
		~Mesh() = default;

		Library::Model& GetModel();
        std::shared_ptr<ModelMaterial> GetMaterial();
        const std::string& Name() const;

		const std::vector<DirectX::XMFLOAT3>& Vertices() const;
		const std::vector<DirectX::XMFLOAT3>& Normals() const;
		const std::vector<DirectX::XMFLOAT3>& Tangents() const;
		const std::vector<DirectX::XMFLOAT3>& BiNormals() const;
		const std::vector<std::vector<DirectX::XMFLOAT3>>& TextureCoordinates() const;
		const std::vector<std::vector<DirectX::XMFLOAT4>>& VertexColors() const;
		std::uint32_t FaceCount() const;
		const std::vector<std::uint32_t>& Indices() const;
		const std::vector<BoneVertexWeights>& BoneWeights() const;

        void CreateIndexBuffer(ID3D11Device& device, gsl::not_null<ID3D11Buffer**> indexBuffer);
		void Save(OutputStreamHelper& streamHelper) const;

    private:
		void Load(InputStreamHelper& streamHelper);

        gsl::not_null<Library::Model*> mModel;
		MeshData mData;
    };
}


#ifndef ABLATE_FLUXDIFFERENCER_H
#define ABLATE_FLUXDIFFERENCER_H
#include <petsc.h>

typedef void (*FluxDifferencerFunction)(PetscReal Mm, PetscReal* sPm, PetscReal* sMm,
                                            PetscReal Mp, PetscReal* sPp, PetscReal *sMp);

#endif


#pragma once

#include <memory>
#include <string>
#include <vector>
#include <typeinfo>
#include <type_traits>

#include <gsl/gsl>

#include <upca/config.h>

#include "arch/arch.h"

namespace upca {

template <typename BACKEND> class resolver {
  class description {

    std::string name_;
    unsigned size_;
    unsigned offset_;
    decltype(typename BACKEND::resolver_type().resolve({})) data_;

  public:
    description(std::string name, const unsigned size, const unsigned offset,
                const typename BACKEND::resolver_type &resolver)
        : name_(std::move(name)), size_(size), offset_(offset),
          data_(resolver.resolve(name_)) {}
    description(const char *name, const unsigned size, const unsigned offset,
                const typename BACKEND::resolver_type &resolver)
        : name_(name), size_(size), offset_(offset),
          data_(resolver.resolve(name_)) {}

    const std::string &name() const { return name_; }
    unsigned size() const { return size_; }
    unsigned offset() const { return offset_; }
    decltype(auto) data() const { return data_; }
  };

  std::vector<description> counters_;
  typename BACKEND::resolver_type resolver_;

public:
  using value_type = description;
  using iterator = typename std::vector<description>::iterator;
  using const_iterator = typename std::vector<description>::const_iterator;

  void add(const char *const name, const unsigned size = 8) {
    counters_.emplace_back(name, size, counters_.size(), resolver_);
  }

  void add(std::string name, const unsigned size = 8) {
    counters_.emplace_back(std::move(name), size, counters_.size(), resolver_);
  }

  size_t size() const { return counters_.size() + 1; }

  unsigned bytesize() const {
    unsigned sum = 0;
    for (const auto &pmc : counters_) {
      sum += pmc.size();
    }
    return sum;
  }

  const description &
  at(const typename std::vector<description>::size_type pos) const {
    return counters_.at(pos);
  }
  const_iterator begin() const { return counters_.cbegin(); }
  const_iterator end() const { return counters_.cend(); }
  const_iterator cbegin() const { return counters_.cbegin(); }
  const_iterator cend() const { return counters_.cend(); }

  /* Get PMU backend. Thread has to be pinned to a CPU before this is called */
  std::unique_ptr<BACKEND> configure(const unsigned additional_counters = 0) const {
    /* meh. add +1 for the "out-of-band" cycles counter :/ */
    return std::make_unique<BACKEND>(counters_, additional_counters + 1);
  }
};

/* sample pmcs */
} // namespace upca


/*
    COPYRIGHT DISCLAIMER

    Vincent Le Guilloux, Peter Schmidtke and Pierre Tuffery, hereby
	disclaim all copyright interest in the program “fpocket” (which
	performs protein cavity detection) written by Vincent Le Guilloux and Peter
	Schmidtke.

    Vincent Le Guilloux  28 November 2008
    Peter Schmidtke      28 November 2008
    Pierre Tuffery       28 November 2008

    GNU GPL

    This file is part of the fpocket package.

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

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

**/

#ifndef DH_UTILS
#define DH_UTILS

/* ------------------------------ INCLUDES ---------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <time.h>

#ifdef MD_USE_GSL 	/* GSL */

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

#endif 				/* /GSL */

#include "memhandler.h"

/* ------------------------------- PUBLIC MACROS ---------------------------- */

#define M_MAX_PDB_NAME_LEN 200  /**< maximum pdb filename length*/

#define M_SIGN 1
#define M_NO_SIGN 0

#ifdef MD_USE_GSL	/* GSL */

#define M_GEN_MTWISTER gsl_rng_mt19937
#define M_GEN_GFSR4 gsl_rng_gfsr4
#define M_GEN_TAUS gsl_rng_taus2
#define M_GEN_RANLXS0 gsl_rng_ranlxs0
#define M_GEN_RANLXS1 gsl_rng_ranlxs1

#endif				/* /GSL */

/* ------------------------------ PUBLIC STRUCTURES ------------------------- */

typedef struct tab_str
{
	char **t_str ;
	int nb_str ;

} tab_str ;

/* ------------------------------- PROTOTYPES ------------------------------- */

int str_is_number(const char *str, const int sign) ;
int str_is_float(const char *str, const int sign) ;
void str_trim(char *str) ;
tab_str* str_split(const char *str, const int sep) ;

tab_str* f_readl(const char *fpath, int nchar_max) ;
void free_tab_str(tab_str *tstr) ;
void print_tab_str(tab_str* strings) ;

int in_tab(int *tab, int size, int val) ;
int index_of(int *tab, int size, int val)  ;

void remove_ext(char *str) ;
void remove_path(char *str) ;
void extract_ext(char *str, char *dest) ;
void extract_path(char *str, char *dest) ;

void start_rand_generator(void) ;
float rand_uniform(float min, float max) ;

FILE* fopen_pdb_check_case(char *name, const char *mode)  ;

float float_get_min_in_2D_array(float **t,size_t n,int col);
float float_get_max_in_2D_array(float **t,size_t n,int col);

#endif


#pragma once

#include <gsl/span>

#include "aas/aas_math.h"

namespace aas {

	class Skeleton;
	struct SkelAnim;
	struct SkelBoneState;

	struct AnimPlayerStreamHeader {
		int field0;
		int field1;
		int field2;
		int8_t pad[3];
	};

	struct AnimPlayerStreamBone
	{
		DX::XMFLOAT4 prevScale;
		DX::XMFLOAT4 scale;
		DX::XMFLOAT4 prevRotation;
		DX::XMFLOAT4 rotation;
		DX::XMFLOAT4 prevTranslation;
		DX::XMFLOAT4 translation;
		int16_t scaleFrame;
		int16_t scaleNextFrame;
		int16_t rotationFrame;
		int16_t rotationNextFrame;
		int16_t translationFrame;
		int16_t translationNextFrame;
		int field6C;
	};

	struct AnimPlayerStream {
		const Skeleton* skeleton;
		const SkelAnim* animation;
		int streamIdx;
		float scaleFactor;
		float translationFactor;
		float currentFrame;
		void* keyframePtr;
		int boneCount;
		AnimPlayerStreamBone bones[1]; //set according to skafile bone count

		AnimPlayerStream(AnimPlayerStream&) = delete;
		AnimPlayerStream(AnimPlayerStream&&) = delete;
		AnimPlayerStream& operator=(AnimPlayerStream&) = delete;
		AnimPlayerStream& operator=(AnimPlayerStream&&) = delete;

		void SetFrame(float frame);
		void Initialize(const SkelAnim *animation, int streamIdx);
		void GetBoneState(gsl::span<SkelBoneState> boneStateOut);
		float GetCurrentFrame() const;
	};

}


/*
 * author: Achim Gaedke
 * created: January 2001
 * file: pygsl/src/histogrammodule.c
 * $Id: histogrammodule.c,v 1.3 2008/10/26 17:03:23 schnizer Exp $
 *
 *
 * May 2005
 *    Pierre Schnizer
 *    maintainance: replaced direct python error calls with pygsl calls
 *    added CAST and GET macros to reduce code duplication
 *
 *    Now the warnings are all handled by a separate function.
 */

#include <pygsl/error_helpers.h>
#include <pygsl/block_helpers.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_histogram2d.h>

enum hist_error{
     NOHIST = 0,
     NOHIST2D,
     ARGNOHIST,
     ARGNOHIST2D,
     HISTP_NULL
};
static const char * filename = __FILE__;
static PyObject * module = NULL;
#include "histogram_doc.ic"

static int
PyGSL_hist_error_helper(const char * function, int line, int myerrno, enum hist_error errtype)
{
     char *tmp;
     switch (errtype){
     case NOHIST:      tmp = "Object was not a histogramm"; break;
     case NOHIST2D:    tmp = "Object was not a 2D histogramm"; break;
     case ARGNOHIST:   tmp = "Argument was not a histogramm"; break;
     case ARGNOHIST2D: tmp = "Argument was not a 2D histogramm"; break;
     case HISTP_NULL:  tmp = "Pointer to GSL histogramm(2d) object was NULL!"; break;
     default:          tmp = "Unknown case in function hist_error_helper"; myerrno = GSL_ESANITY; break;
     }
     PyGSL_add_traceback(module, filename, function, line);
     pygsl_error(tmp, filename, line, myerrno);
     return myerrno;
}

/*
 * Check if the recieved object is of approbriate type and that the histogram is defined.
 * Invokes PyGSL_hist_error_helper if it fails.
 * returns GSL_SUCCESS on success
 *
 * ob      ... the object to check
 * type    ... the Python type object
 * errcode ... the errorcode used to describe the failure type for
 *             PyGSL_hist_error_helper
 */

#define _PyGSL_HIST_CHECK_INT(ob, type, errcode) \
     ((ob->ob_type == &(type)) ?  GSL_SUCCESS :  \
     PyGSL_hist_error_helper(__FUNCTION__, __LINE__, (errcode), GSL_ESANITY))

/*
 * returns the gsl histogram from the object
 */
#define _PyGSL_HIST_CAST(ob, type) (( ((type *)(ob)) ->h ))

/*
 * Check if it the gsl_histogram is not NULL. Will give a descriptive error
 * message if it fails.
 */
#define _PyGSL_HIST_CAST_SAVE(ob, type) \
      (\
          (_PyGSL_HIST_CAST((ob), type)  == NULL ) \
        ? \
          (PyGSL_hist_error_helper(__FUNCTION__, __LINE__, GSL_EFAULT, HISTP_NULL), NULL) \
        : \
          ( _PyGSL_HIST_CAST((ob), type) ) \
      )

/*
 * Try to get the gsl_histogram from the python object.
 * Checks for errors ...
 */
#define _PyGSL_HIST_GET_INT(ob, type, cast, errcode) \
      ( \
          (  _PyGSL_HIST_CHECK_INT((ob), type, (errcode)) == GSL_SUCCESS ) \
        ? \
          _PyGSL_HIST_CAST_SAVE((ob), cast) \
	: \
          NULL \
      )
#define _PyGSL_HIST_GET(ob, type, errcode) _PyGSL_HIST_GET_INT(ob, type ## Type, type ## Object, errcode)

#define _PyGSL_HIST_CHECK(ob, errcode)   _PyGSL_HIST_CHECK_INT((ob), (histogram_histogramType), (errcode))
#define _PyGSL_HIST2D_CHECK(ob, errcode) _PyGSL_HIST_CHECK_INT((ob), (histogram_histogram2dType), (errcode))

#define PyGSL_HIST_CHECK(ob)       _PyGSL_HIST_CHECK((ob), (NOHIST))
#define PyGSL_HIST2D_CHECK(ob)     _PyGSL_HIST2D_CHECK((ob), (NOHIST2D))
#define PyGSL_HIST_ARG_CHECK(ob)   _PyGSL_HIST_CHECK((ob), (ARGNOHIST))
#define PyGSL_HIST2D_ARG_CHECK(ob) _PyGSL_HIST2D_CHECK((ob), (ARGNOHIST2D))

#define PyGSL_HIST_CAST(ob)   _PyGSL_HIST_CAST((ob), histogram_histogramObject)
#define PyGSL_HIST2D_CAST(ob) _PyGSL_HIST_CAST((ob), histogram_histogram2dObject)

#define PyGSL_HIST_GET(ob)        _PyGSL_HIST_GET((ob), histogram_histogram,   (NOHIST))
#define PyGSL_HIST2D_GET(ob)      _PyGSL_HIST_GET((ob), histogram_histogram2d, (NOHIST2D))
#define _PyGSL_HIST_GET_ARG(ob)   _PyGSL_HIST_GET((ob), histogram_histogram,   (ARGNOHIST))
#define _PyGSL_HIST2D_GET_ARG(ob) _PyGSL_HIST_GET((ob), histogram_histogram2d, (ARGNOHIST2D))

#define PyGSL_HIST_GET_ARG(ob)   ( ((ob) == NULL) ? NULL : _PyGSL_HIST_GET_ARG((ob)) )
#define PyGSL_HIST2D_GET_ARG(ob) ( ((ob) == NULL) ? NULL : _PyGSL_HIST2D_GET_ARG((ob)) )

/*
 * Helper function for dealing with warning and errors ...
 */
static int
PyGSL_warn_err(int rcode, int errcode, const char * errdes, const char * file, int line)
{
     int warn_result;
     if (errcode==rcode) {
	  warn_result = PyGSL_warning(errdes, file, line, errcode);
	  if (warn_result==-1)
	       /* exception is raised by PyErr_Warn */
	       return GSL_EFAILED;
     }
     else if (PyGSL_ERROR_FLAG(rcode) != GSL_SUCCESS)
	  return rcode;

     return GSL_SUCCESS;
}

#define PyGSL_WARN_ERR(ob, errcode, errdes) \
   (\
       ((ob) == (GSL_SUCCESS)) \
      ? \
         GSL_SUCCESS \
      : \
          ((ob) == (errcode)) \
        ? PyGSL_warn_err(ob, errcode, errdes, filename, __LINE__) \
        : PyGSL_error_flag((ob)) \
    )

static const char edom_message[] =  "value out of histogram range";
#define PyGSL_HIST_EDOM_WARN(ob) PyGSL_WARN_ERR((ob), GSL_EDOM, edom_message)

typedef int (*hist_op)(void *, const void *);
typedef int (*hist_file)(FILE *, void *);

static PyObject *
histogram_histogram_op(PyObject *self, PyObject * arg, hist_op fptr);
static PyObject *
histogram_histogram2d_op(PyObject *self, PyObject * arg, hist_op fptr);
static PyObject *
histogram_histogram_file(PyObject *self, PyObject * arg, hist_file fptr);
static PyObject *
histogram_histogram2d_file(PyObject *self, PyObject * arg, hist_file fptr);

/*
 *
 * histogram type
 * for 1d histogram data
 *
 */

/* my typedef */

staticforward PyTypeObject histogram_histogramType;
staticforward PyMethodDef histogram_histogram_methods[];

typedef struct {
    PyObject_HEAD
    gsl_histogram* h;
} histogram_histogramObject;

#define _CONCAT2(class, suffix) class ## _ ## suffix

#include "histogram.ic"

#define HISTTYPE gsl_histogram
#define PyGSLHISTTYPE histogram_histogramType
#define PyGSL_HIST_TYPE_GET(ob) PyGSL_HIST_GET((ob))
#define PyGSL_HIST_TYPE_CAST(ob) PyGSL_HIST_CAST((ob))
#define PyGSL_HIST_TYPE_ARG_GET(ob) PyGSL_HIST_GET_ARG((ob))
#define GSLNAME(suffix)  _CONCAT2(gsl_histogram, suffix)
#define FUNCNAME(suffix) _CONCAT2(histogram_histogram, suffix)
#ifdef HISTOGRAM2D
#undef HISTOGRAM2D
#endif
#include "histogram_pdf_common.ic"
#include "histogram_common.ic"

#undef HISTTYPE
#undef PyGSLHISTTYPE
#undef PyGSL_HIST_TYPE_GET
#undef PyGSL_HIST_TYPE_CAST
#undef PyGSL_HIST_TYPE_ARG_GET
#undef FUNCNAME
#undef GSLNAME

static
PyTypeObject histogram_histogramType = {
	PyObject_HEAD_INIT(NULL)
	0,
	"pygsl.histogram.histogram",
	sizeof(histogram_histogramObject),
	0,
	(destructor)histogram_histogram_dealloc, /* tp_dealloc */
	0,                                      /* tp_print */
	histogram_histogram_getattr,            /* tp_getattr */
	0,					/* tp_setattr */
	0,			                /* tp_compare */
	0,                  			/* tp_repr */
	0,					/* tp_as_number */
	0,	                		/* tp_as_sequence */
	&histogram_histogram_as_mapping,	/* tp_as_mapping */
	0,				        /* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	0,		                        /* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT,		        /* tp_flags */
        0,				        /* tp_doc */
	0, 		                        /* tp_traverse */
	0,			                /* tp_clear */
	0,              			/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,			                /* tp_iter */
	0,					/* tp_iternext */
	0,				        /* tp_methods */
	0,					/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	(initproc)histogram_histogram_init,	/* tp_init */
	NULL,              			/* tp_alloc */
	NULL,                			/* tp_new */
	NULL         			        /* tp_free */
};

/*
 *
 * here begins the section for the 2d histogram
 *
 */

/* my typedef */

staticforward PyTypeObject histogram_histogram2dType;
staticforward PyMethodDef histogram_histogram2d_methods[];
typedef struct {
    PyObject_HEAD
    gsl_histogram2d* h;
} histogram_histogram2dObject;

static PyObject *
histogram_histogram2d_reset(PyObject *);

#include "histogram2d.ic"
#define HISTOGRAM2D 1
#define HISTTYPE gsl_histogram2d
#define PyGSLHISTTYPE histogram_histogram2dType
#define PyGSL_HIST_TYPE_GET(ob) PyGSL_HIST2D_GET((ob))
#define PyGSL_HIST_TYPE_ARG_GET(ob) PyGSL_HIST2D_GET_ARG((ob))
#define PyGSL_HIST_TYPE_CAST(ob) PyGSL_HIST2D_CAST((ob))
#define GSLNAME(suffix)  _CONCAT2(gsl_histogram2d, suffix)
#define FUNCNAME(suffix) _CONCAT2(histogram_histogram2d, suffix)
#include "histogram_pdf_common.ic"
#include "histogram_common.ic"

static
PyTypeObject histogram_histogram2dType = {
	PyObject_HEAD_INIT(NULL)
	0,
	"pygsl.histogram.histogram2d",
	sizeof(histogram_histogram2dObject),
	0,
	(destructor)histogram_histogram2d_dealloc, /* tp_dealloc */
	0,                                      /* tp_print */
	histogram_histogram2d_getattr,          /* tp_getattr */
	0,					/* tp_setattr */
	0,			                /* tp_compare */
	0,                  			/* tp_repr */
	0,					/* tp_as_number */
	0,	                		/* tp_as_sequence */
	&histogram_histogram2d_as_mapping,      /* tp_as_mapping */
	0,				        /* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	0,		                        /* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT,		        /* tp_flags */
        0,				        /* tp_doc */
	0, 		                        /* tp_traverse */
	0,			                /* tp_clear */
	0,              			/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,			                /* tp_iter */
	0,					/* tp_iternext */
	0,				        /* tp_methods */
	0,					/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	(initproc)histogram_histogram2d_init,	/* tp_init */
	NULL,            			/* tp_alloc */
	NULL,			                /* tp_new */
	NULL			                /* tp_free */
};

#include "histogram_pdf.ic"
/*
 *
 * module specific stuff
 *
 */

static PyMethodDef histogramMethods[] = {
  {NULL, NULL, 0, NULL}        /* Sentinel */
};

void
register_type(PyTypeObject *p, char *name)
{
     p->ob_type  = &PyType_Type;
     p->tp_alloc = PyType_GenericAlloc;
     p->tp_new   = PyType_GenericNew;
     p->tp_free  = _PyObject_Del;
     /* install histogram type */
     /* important! must increment histogram type reference counter */
     Py_INCREF((PyObject*)p);
     PyModule_AddObject(module, name, (PyObject*)p);
}

void
inithistogram(void)
{
  PyObject* m;
  m=Py_InitModule("histogram", histogramMethods);
  if(!m)
       return;

  module = m;
  init_pygsl();
  /* init histogram type */
  register_type(&histogram_histogramType, "histogram");
  register_type(&histogram_histogram_pdfType, "histogram_pdf");
  register_type(&histogram_histogram2dType, "histogram2d");
  register_type(&histogram_histogram2d_pdfType, "histogram2d_pdf");
}


///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// 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 BITVEC_DECL_H
#define BITVEC_DECL_H

#include <gsl/gsl_assert> // for Expects
#include <gsl/gsl_util>   // for narrow_cast, narrow

using gsl::narrow_cast;

#include <cstddef>  // for ptrdiff_t, size_t, nullptr_t
#include <iterator> // for reverse_iterator, distance, random_access_...
#include <memory>

#ifdef _MSC_VER
#pragma warning(push)

// turn off some warnings that are noisy about our Expects statements
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code

// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool.
#pragma warning(                                                               \
    disable : 26495) // uninitalized member when constructor calls constructor
#pragma warning(                                                               \
    disable : 26446) // parser bug does not allow attributes on some templates

#endif // _MSC_VER

namespace gb {

// shared_ptr without atomic overhead
template <typename T>
using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;

// [views.constants], constants
const std::ptrdiff_t dynamic_extent = -1;

template <std::ptrdiff_t Extent = dynamic_extent> class bitvec;

// implementation details
namespace details {
template <class Span, bool IsConst> class span_iterator {
	using element_type_ = typename Span::element_type;

  public:
#ifdef _MSC_VER
	// Tell Microsoft standard library that span_iterators are checked.
	using _Unchecked_type = typename Span::pointer;
#endif

	using iterator_category = std::random_access_iterator_tag;
	using value_type = std::remove_cv_t<element_type_>;
	using difference_type = typename Span::index_type;

	using pointer = shared_ptr_unsynchronized<
	    std::conditional_t<IsConst, const element_type_, element_type_>>;

	span_iterator() = default;

	span_iterator(const Span *bitvec, typename Span::index_type idx) noexcept
	    : span_(bitvec), index_(idx) {}

	friend span_iterator<Span, true>;
	template <bool B, std::enable_if_t<!B && IsConst> * = nullptr>
	span_iterator(const span_iterator<Span, B> &other) noexcept
	    : span_iterator(other.span_, other.index_) {}

	std::conditional_t<IsConst, const bitvec<1>, bitvec<1>> operator*() const {
		Expects(index_ != span_->size());
		return std::conditional_t<IsConst, const bitvec<1>, bitvec<1>>(
		    (*span_)[index_]);
	}

	pointer operator->() const {
		Expects(index_ != span_->size());
		return span_->data() + index_;
	}

	span_iterator &operator++() {
		Expects(0 <= index_ && index_ != span_->size());
		++index_;
		return *this;
	}

	span_iterator &operator--() {
		Expects(index_ != 0 && index_ <= span_->size());
		--index_;
		return *this;
	}

	pointer operator[](difference_type n) const { return *(*this + n); }

	friend bool operator==(span_iterator lhs, span_iterator rhs) noexcept {
		return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_;
	}

	friend bool operator!=(span_iterator lhs, span_iterator rhs) noexcept {
		return !(lhs == rhs);
	}

	friend bool operator<(span_iterator lhs, span_iterator rhs) noexcept {
		return lhs.index_ < rhs.index_;
	}

#ifdef _MSC_VER
	// MSVC++ iterator debugging support; allows STL algorithms in 15.8+
	// to unwrap span_iterator to a pointer type after a range check in STL
	// algorithm calls
	friend void _Verify_range(
	    span_iterator lhs,
	    span_iterator rhs) noexcept {  // test that [lhs, rhs) forms a valid
		                               // range inside an STL algorithm
		Expects(lhs.span_ == rhs.span_ // range spans have to match
		        && lhs.index_ <= rhs.index_); // range must not be transposed
	}

	void _Verify_offset(const difference_type n) const
	    noexcept { // test that the iterator *this + n is a valid range in an
		           // STL
		// algorithm call
		Expects((index_ + n) >= 0 && (index_ + n) <= span_->size());
	}

	GSL_SUPPRESS(bounds .1)               // NO-FORMAT: attribute
	pointer _Unwrapped() const noexcept { // after seeking *this to a high water
		                                  // mark, or using one of the
		// _Verify_xxx functions above, unwrap this span_iterator to a raw
		// pointer
		return span_->data() + index_;
	}

	// Tell the STL that span_iterator should not be unwrapped if it can't
	// validate in advance, even in release / optimized builds:
	static const bool _Unwrap_when_unverified = false;
	GSL_SUPPRESS(con .3) // NO-FORMAT: attribute // TODO: false positive
	void _Seek_to(const pointer p) noexcept { // adjust the position of *this to
		                                      // previously verified location p
		// after _Unwrapped
		index_ = p - span_->data();
	}
#endif

  protected:
	const Span *span_ = nullptr;
	std::ptrdiff_t index_ = 0;
};

// Used for empty base class optimization
template <std::ptrdiff_t Ext> class extent_type {
  public:
	using index_type = std::ptrdiff_t;

	static_assert(Ext >= 0, "A fixed-size bitvec must be >= 0 in size.");

	extent_type() noexcept {}

	template <index_type Other> extent_type(extent_type<Other> ext) {
		static_assert(Other == Ext || Other == dynamic_extent,
		              "Mismatch between fixed-size extent and size of "
		              "initializing data.");
		Expects(ext.size() == Ext);
	}

	extent_type(index_type size) { Expects(size == Ext); }

	index_type size() const noexcept { return Ext; }
};

template <> class extent_type<dynamic_extent> {
  public:
	using index_type = std::ptrdiff_t;

	template <index_type Other>
	explicit extent_type(extent_type<Other> ext) : size_(ext.size()) {}

	explicit extent_type(index_type size) : size_(size) { Expects(size >= 0); }

	index_type size() const noexcept { return size_; }

  private:
	index_type size_;
};

template <std::ptrdiff_t Extent, std::ptrdiff_t Offset, std::ptrdiff_t Count>
struct calculate_subspan_type {
	using type =
	    bitvec<Count != dynamic_extent
	             ? Count
	             : (Extent != dynamic_extent ? Extent - Offset : Extent)>;
};
} // namespace details

} // namespace gb

#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER

#endif // BITVEC_DECL_H


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

#pragma once

#include "core/common/logging/logging.h"
#include "core/framework/allocatormgr.h"
#include "core/framework/customregistry.h"
#include "core/framework/execution_frame.h"
#include "core/framework/op_kernel.h"
#include "core/framework/run_options.h"
#include "core/framework/session_state.h"
#include "core/framework/tensor.h"
#include "core/graph/graph_viewer.h"
#include "core/graph/model.h"
#include "core/framework/data_types.h"
#include "test/test_environment.h"
#include "test/framework/TestAllocatorManager.h"
#include "core/framework/TensorSeq.h"
#include "core/framework/session_options.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <gsl/gsl>
#include "core/util/math_cpuonly.h"

// helpers to run a function and check the status, outputting any error if it fails.
// note: wrapped in do{} while(false) so the _tmp_status variable has limited scope
#define ASSERT_STATUS_OK(function)                  \
  do {                                              \
    auto _tmp_status = function;                    \
    ASSERT_TRUE(_tmp_status.IsOK()) << _tmp_status; \
  } while (false)

#define EXPECT_STATUS_OK(function)                  \
  do {                                              \
    auto _tmp_status = function;                    \
    EXPECT_TRUE(_tmp_status.IsOK()) << _tmp_status; \
  } while (false)

namespace onnxruntime {
class InferenceSession;
struct SessionOptions;

namespace test {
template <typename T>
struct SeqTensors {
  void AddTensor(const std::vector<int64_t>& shape0, const std::vector<T>& data0) {
    tensors.push_back(Tensor<T>{shape0, data0});
  }

  template <typename U>
  struct Tensor {
    std::vector<int64_t> shape;
    std::vector<U> data;
  };
  std::vector<Tensor<T>> tensors;
};

// unfortunately std::optional is in C++17 so use a miniversion of it
template <typename T>
class optional {
 public:
  optional(T v) : has_value_(true), value_(v) {}
  optional() : has_value_(false) {}
  bool has_value() const { return has_value_; }
  const T& value() const {
    ORT_ENFORCE(has_value_);
    return value_;
  }

 private:
  bool has_value_;
  T value_;
};

// Function templates to translate C++ types into ONNX_NAMESPACE::TensorProto_DataTypes
template <typename T>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType();

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<float>() {
  return ONNX_NAMESPACE::TensorProto_DataType_FLOAT;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<double>() {
  return ONNX_NAMESPACE::TensorProto_DataType_DOUBLE;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<int32_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_INT32;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<int64_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_INT64;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<bool>() {
  return ONNX_NAMESPACE::TensorProto_DataType_BOOL;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<int8_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_INT8;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<int16_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_INT16;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<uint8_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_UINT8;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<uint16_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_UINT16;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<uint32_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_UINT32;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<uint64_t>() {
  return ONNX_NAMESPACE::TensorProto_DataType_UINT64;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<std::string>() {
  return ONNX_NAMESPACE::TensorProto_DataType_STRING;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<MLFloat16>() {
  return ONNX_NAMESPACE::TensorProto_DataType_FLOAT16;
}

template <>
constexpr ONNX_NAMESPACE::TensorProto_DataType TypeToDataType<BFloat16>() {
  return ONNX_NAMESPACE::TensorProto_DataType_BFLOAT16;
}

template <typename T>
struct TTypeProto : ONNX_NAMESPACE::TypeProto {
  TTypeProto(const std::vector<int64_t>* shape = nullptr) {
    mutable_tensor_type()->set_elem_type(TypeToDataType<T>());

    if (shape) {
      auto mutable_shape = mutable_tensor_type()->mutable_shape();
      for (auto i : *shape) {
        auto* mutable_dim = mutable_shape->add_dim();
        if (i != -1)
          mutable_dim->set_dim_value(i);
        else
          mutable_dim->set_dim_param("symbolic");
      }
    }
  }
};

// Variable template for ONNX_NAMESPACE::TensorProto_DataTypes, s_type_proto<float>, etc..
template <typename T>
struct TTensorType {
  static const TTypeProto<T> s_type_proto;
};

template <typename T>
const TTypeProto<T> TTensorType<T>::s_type_proto;

// TypeProto for map<TKey, TVal>
template <typename TKey, typename TVal>
struct MTypeProto : ONNX_NAMESPACE::TypeProto {
  MTypeProto() {
    mutable_map_type()->set_key_type(TypeToDataType<TKey>());
    mutable_map_type()->mutable_value_type()->mutable_tensor_type()->set_elem_type(TypeToDataType<TVal>());
    mutable_map_type()->mutable_value_type()->mutable_tensor_type()->mutable_shape()->clear_dim();
  }
};

template <typename TKey, typename TVal>
struct MMapType {
  static const MTypeProto<TKey, TVal> s_map_type_proto;
};

template <typename TKey, typename TVal>
const MTypeProto<TKey, TVal> MMapType<TKey, TVal>::s_map_type_proto;

// TypeProto for vector<map<TKey, TVal>>
template <typename TKey, typename TVal>
struct VectorOfMapTypeProto : ONNX_NAMESPACE::TypeProto {
  VectorOfMapTypeProto() {
    auto* map_type = mutable_sequence_type()->mutable_elem_type()->mutable_map_type();
    map_type->set_key_type(TypeToDataType<TKey>());
    map_type->mutable_value_type()->mutable_tensor_type()->set_elem_type(TypeToDataType<TVal>());
    map_type->mutable_value_type()->mutable_tensor_type()->mutable_shape()->clear_dim();
  }
};

template <typename TKey, typename TVal>
struct VectorOfMapType {
  static const VectorOfMapTypeProto<TKey, TVal> s_vec_map_type_proto;
};

template <typename TKey, typename TVal>
const VectorOfMapTypeProto<TKey, TVal> VectorOfMapType<TKey, TVal>::s_vec_map_type_proto;

template <typename ElemType>
struct SequenceTensorTypeProto : ONNX_NAMESPACE::TypeProto {
  SequenceTensorTypeProto() {
    MLDataType dt = DataTypeImpl::GetTensorType<ElemType>();
    const auto* elem_proto = dt->GetTypeProto();
    mutable_sequence_type()->mutable_elem_type()->CopyFrom(*elem_proto);
    auto* tensor_type = mutable_sequence_type()->mutable_elem_type()->mutable_tensor_type();
    tensor_type->set_elem_type(TypeToDataType<ElemType>());
  }
};

template <typename ElemType>
struct SequenceTensorType {
  static const SequenceTensorTypeProto<ElemType> s_sequence_tensor_type_proto;
};

template <typename ElemType>
const SequenceTensorTypeProto<ElemType> SequenceTensorType<ElemType>::s_sequence_tensor_type_proto;

// To use OpTester:
//  1. Create one with the op name
//  2. Call AddAttribute with any attributes
//  3. Call AddInput for all the inputs
//  4. Call AddOutput with all expected outputs
//  5. Call Run
// Not all tensor types and output types are added, if a new input type is used, add it to the TypeToDataType list
// above for new output types, add a new specialization for Check<> See current usage for an example, should be self
// explanatory
class OpTester {
 public:
  // Default to the first opset that ORT was available (7).
  // When operators are updated they need to explicitly add tests for the new opset version.
  // This is due to the kernel matching logic. See KernelRegistry::VerifyKernelDef.
  // Additionally, -1 is supported and defaults to the latest known opset.
  //
  // Defaulting to the latest opset version would result in existing operator implementations for non-CPU EPs to
  // lose their test coverage until an implementation for the new version is added.
  //   e.g. there are CPU and GPU implementations for version 1 of an op. both are tested by a single OpTester test.
  //        opset changes from 1 to 2 and CPU implementation gets added. If 'opset_version' is 2 the kernel matching
  //        will find and run the CPU v2 implementation, but will not match the GPU v1 implementation.
  //        OpTester will say it was successful as at least one EP ran, and the GPU implementation of v1 no longer has
  //        test coverage.
  explicit OpTester(const char* op, int opset_version = 7, const char* domain = onnxruntime::kOnnxDomain)
      : op_(op), domain_(domain), opset_version_(opset_version) {
    if (opset_version_ < 0) {
      static int latest_onnx_version =
          ONNX_NAMESPACE::OpSchemaRegistry::DomainToVersionRange().Map().at(ONNX_NAMESPACE::ONNX_DOMAIN).second;
      opset_version_ = latest_onnx_version;
    }
  }

  ~OpTester();

  // Set whether the NodeArg created by AddInput/AddOutput should include shape information
  // for Tensor types. If not added, shape inferencing should resolve. If added, shape inferencing
  // should validate. Default is to not add.
  // Additionally a symbolic dimension will be added if symbolic_dim matches a dimension in the input.
  OpTester& AddShapeToTensorData(bool add_shape = true, int symbolic_dim = -1) {
    add_shape_to_tensor_data_ = add_shape;
    add_symbolic_dim_to_tensor_data_ = symbolic_dim;
    return *this;
  }

  // We have an initializer_list and vector version of the Add functions because std::vector is specialized for
  // bool and we can't get the raw data out. So those cases must use an initializer_list
  template <typename T>
  void AddInput(const char* name, const std::vector<int64_t>& dims, const std::initializer_list<T>& values,
                bool is_initializer = false) {
    AddData(input_data_, name, dims, values.begin(), values.size(), is_initializer);
  }

  template <typename T>
  void AddInput(const char* name, const std::vector<int64_t>& dims, const std::vector<T>& values,
                bool is_initializer = false) {
    AddData(input_data_, name, dims, values.data(), values.size(), is_initializer);
  }

  // Add other registered types, possibly experimental
  template <typename T>
  void AddInput(const char* name, const T& val) {
    auto mltype = DataTypeImpl::GetType<T>();
    ORT_ENFORCE(mltype != nullptr, "T must be a registered cpp type");
    auto ptr = onnxruntime::make_unique<T>(val);
    OrtValue value;
    value.Init(ptr.get(), mltype, mltype->GetDeleteFunc());
    ptr.release();
    input_data_.push_back(Data(NodeArg(name, mltype->GetTypeProto()), std::move(value), optional<float>(),
                               optional<float>()));
  }

  template <typename T>
  void AddInput(const char* name, T&& val) {
    auto mltype = DataTypeImpl::GetType<T>();
    ORT_ENFORCE(mltype != nullptr, "T must be a registered cpp type");
    auto ptr = onnxruntime::make_unique<T>(std::move(val));
    OrtValue value;
    value.Init(ptr.get(), mltype, mltype->GetDeleteFunc());
    ptr.release();
    input_data_.push_back(Data(NodeArg(name, mltype->GetTypeProto()), std::move(value), optional<float>(),
                               optional<float>()));
  }

  template <typename T>
  void AddSeqInput(const char* name, const SeqTensors<T>& seq_tensors) {
    AddSeqData<T>(input_data_, name, seq_tensors);
  }

  template <typename T>
  void AddSeqOutput(const char* name, const SeqTensors<T>& seq_tensors) {
    AddSeqData<T>(output_data_, name, seq_tensors);
  }

  template <typename TKey, typename TVal>
  void AddInput(const char* name, const std::map<TKey, TVal>& val) {
    std::unique_ptr<std::map<TKey, TVal>> ptr = onnxruntime::make_unique<std::map<TKey, TVal>>(val);
    OrtValue value;
    value.Init(ptr.release(), DataTypeImpl::GetType<std::map<TKey, TVal>>(),
               DataTypeImpl::GetType<std::map<TKey, TVal>>()->GetDeleteFunc());
    input_data_.push_back(Data(NodeArg(name, &MMapType<TKey, TVal>::s_map_type_proto), std::move(value),
                               optional<float>(), optional<float>()));
  }

  template <typename T>
  void AddMissingOptionalInput() {
    std::string name;  // empty == input doesn't exist
    input_data_.push_back(Data(NodeArg(name, &TTensorType<T>::s_type_proto), OrtValue(), optional<float>(),
                               optional<float>()));
  }

  template <typename T>
  void AddOutput(const char* name, const std::vector<int64_t>& dims, const std::initializer_list<T>& expected_values,
                 bool sort_output = false) {
    AddData(output_data_, name, dims, expected_values.begin(), expected_values.size(), false, sort_output);
  }

  template <typename T>
  void AddOutput(const char* name, const std::vector<int64_t>& dims, const std::vector<T>& expected_values,
                 bool sort_output = false) {
    AddData(output_data_, name, dims, expected_values.data(), expected_values.size(), false, sort_output);
  }

  template <typename T>
  void AddMissingOptionalOutput() {
    std::string name;  // empty == input doesn't exist
    output_data_.push_back(Data(NodeArg(name, &TTensorType<T>::s_type_proto), OrtValue(), optional<float>(),
                                optional<float>()));
  }

  // Add other registered types, possibly experimental
  template <typename T>
  void AddOutput(const char* name, const T& val) {
    auto mltype = DataTypeImpl::GetType<T>();
    ORT_ENFORCE(mltype != nullptr, "T must be a registered cpp type");
    auto ptr = onnxruntime::make_unique<T>(val);
    OrtValue value;
    value.Init(ptr.get(), mltype, mltype->GetDeleteFunc());
    ptr.release();
    output_data_.push_back(Data(NodeArg(name, mltype->GetTypeProto()), std::move(value), optional<float>(),
                                optional<float>()));
  }

  template <typename T>
  void AddOutput(const char* name, T&& val) {
    auto mltype = DataTypeImpl::GetType<T>();
    ORT_ENFORCE(mltype != nullptr, "T must be a registered cpp type");
    auto ptr = onnxruntime::make_unique<T>(std::move(val));
    OrtValue value;
    value.Init(ptr.get(), mltype, mltype->GetDeleteFunc());
    ptr.release();
    output_data_.push_back(Data(NodeArg(name, mltype->GetTypeProto()), std::move(value), optional<float>(),
                                optional<float>()));
  }

  // Add non tensor output
  template <typename TKey, typename TVal>
  void AddOutput(const char* name, const std::vector<std::map<TKey, TVal>>& val) {
    auto ptr = onnxruntime::make_unique<std::vector<std::map<TKey, TVal>>>(val);
    OrtValue ml_value;
    ml_value.Init(ptr.release(), DataTypeImpl::GetType<std::vector<std::map<TKey, TVal>>>(),
                  DataTypeImpl::GetType<std::vector<std::map<TKey, TVal>>>()->GetDeleteFunc());
    output_data_.push_back(Data(NodeArg(name, &VectorOfMapType<TKey, TVal>::s_vec_map_type_proto), std::move(ml_value),
                                optional<float>(), optional<float>()));
  }

  void AddCustomOpRegistry(std::shared_ptr<CustomRegistry> registry) {
    custom_schema_registries_.push_back(registry->GetOpschemaRegistry());
    custom_session_registries_.push_back(registry);
  }

  void SetOutputAbsErr(const char* name, float v);
  void SetOutputRelErr(const char* name, float v);

  // Number of times to call InferenceSession::Run. The same feeds are used each time.
  // e.g. used to verify the generator ops behave as expected
  void SetNumRunCalls(int n) {
    ORT_ENFORCE(n > 0);
    num_run_calls_ = n;
  }

  template <typename T>
  void AddAttribute(std::string name, T value) {
    // Generate a the proper AddAttribute call for later
    add_attribute_funcs_.emplace_back([name = std::move(name), value = std::move(value)](onnxruntime::Node& node) {
      node.AddAttribute(name, value);
    });
  }

  enum class ExpectResult { kExpectSuccess,
                            kExpectFailure };

  void Run(ExpectResult expect_result = ExpectResult::kExpectSuccess, const std::string& expected_failure_string = "",
           const std::unordered_set<std::string>& excluded_provider_types = {},
           const RunOptions* run_options = nullptr,
           std::vector<std::unique_ptr<IExecutionProvider>>* execution_providers = nullptr,
           ExecutionMode execution_mode = ExecutionMode::ORT_SEQUENTIAL);

  void Run(SessionOptions session_options,
           ExpectResult expect_result = ExpectResult::kExpectSuccess,
           const std::string& expected_failure_string = "",
           const std::unordered_set<std::string>& excluded_provider_types = {},
           const RunOptions* run_options = nullptr,
           std::vector<std::unique_ptr<IExecutionProvider>>* execution_providers = nullptr);

  struct Data {
    onnxruntime::NodeArg def_;
    OrtValue data_;
    optional<float> relative_error_;
    optional<float> absolute_error_;
    bool sort_output_;
    Data(onnxruntime::NodeArg&& def, OrtValue&& data, optional<float>&& rel, optional<float>&& abs,
         bool sort_output = false)
        : def_(std::move(def)),
          data_(std::move(data)),
          relative_error_(std::move(rel)),
          absolute_error_(abs),
          sort_output_(sort_output) {}
    Data(Data&&) = default;
    Data& operator=(Data&&) = default;
  };

 protected:
  virtual void AddNodes(onnxruntime::Graph& graph, std::vector<onnxruntime::NodeArg*>& graph_input_defs,
                        std::vector<onnxruntime::NodeArg*>& graph_output_defs,
                        std::vector<std::function<void(onnxruntime::Node& node)>>& add_attribute_funcs);

  void AddInitializers(onnxruntime::Graph& graph);

  void FillFeedsAndOutputNames(std::unordered_map<std::string, OrtValue>& feeds,
                               std::vector<std::string>& output_names);

  std::unique_ptr<onnxruntime::Model> BuildGraph();

  const char* op_;

#ifndef NDEBUG
  bool run_called_{};
#endif

 private:
  template <typename T>
  void AddData(std::vector<Data>& data, const char* name, const std::vector<int64_t>& dims, const T* values,
               int64_t values_count, bool is_initializer = false, bool sort_output = false) {
    try {
      TensorShape shape{dims};
      ORT_ENFORCE(shape.Size() == values_count, values_count, " input values doesn't match tensor size of ",
                  shape.Size());

      auto allocator = test::AllocatorManager::Instance().GetAllocator(CPU);
      auto p_tensor = onnxruntime::make_unique<Tensor>(DataTypeImpl::GetType<T>(), shape, allocator);

      auto* data_ptr = p_tensor->template MutableData<T>();
      for (int64_t i = 0; i < values_count; i++) {
        data_ptr[i] = values[i];
      }

      std::vector<int64_t> dims_for_proto{dims};
      if (add_symbolic_dim_to_tensor_data_ >= 0 &&
          dims.size() > static_cast<size_t>(add_symbolic_dim_to_tensor_data_)) {
        dims_for_proto[add_symbolic_dim_to_tensor_data_] = -1;
      }

      TTypeProto<T> type_proto(add_shape_to_tensor_data_ ? &dims_for_proto : nullptr);
      OrtValue value;
      value.Init(p_tensor.release(), DataTypeImpl::GetType<Tensor>(),
                 DataTypeImpl::GetType<Tensor>()->GetDeleteFunc());
      data.push_back(Data(NodeArg(name, &type_proto), std::move(value), optional<float>(), optional<float>(), sort_output));
      if (is_initializer) initializer_index_.push_back(data.size() - 1);
    } catch (const std::exception& ex) {
      std::cerr << "AddData for '" << name << "' threw: " << ex.what();
      throw;
    }
  }

  template <typename T>
  void AddSeqData(std::vector<Data>& data, const char* name, const SeqTensors<T>& seq_tensors) {
    auto num_tensors = seq_tensors.tensors.size();
    std::vector<Tensor> tensors;
    tensors.resize(num_tensors);
    auto elem_type = DataTypeImpl::GetType<T>();
    for (size_t i = 0; i < num_tensors; ++i) {
      TensorShape shape{seq_tensors.tensors[i].shape};
      auto values_count = static_cast<int64_t>(seq_tensors.tensors[i].data.size());
      ORT_ENFORCE(shape.Size() == values_count, values_count,
                  " input values doesn't match tensor size of ", shape.Size());

      auto allocator = test::AllocatorManager::Instance().GetAllocator(CPU);
      auto& tensor = tensors[i];

      tensor = Tensor(elem_type,
                      shape,
                      allocator);

      auto* data_ptr = tensor.template MutableData<T>();
      for (int64_t x = 0; x < values_count; ++x) {
        data_ptr[x] = seq_tensors.tensors[i].data[x];
      }
    }

    OrtValue value;
    auto mltype = DataTypeImpl::GetType<TensorSeq>();
    auto ptr = onnxruntime::make_unique<TensorSeq>(elem_type);
    ptr->SetElements(std::move(tensors));
    value.Init(ptr.get(), mltype, mltype->GetDeleteFunc());
    ptr.release();
    data.push_back(Data(NodeArg(name, &SequenceTensorType<T>::s_sequence_tensor_type_proto), std::move(value),
                        optional<float>(), optional<float>()));
  }

  void ExecuteModel(Model& model, InferenceSession& session_object, ExpectResult expect_result,
                    const std::string& expected_failure_string, const RunOptions* run_options,
                    std::unordered_map<std::string, OrtValue> feeds, std::vector<std::string> output_names,
                    const std::string& provider_type);

  const char* domain_;
  int opset_version_;
  bool add_shape_to_tensor_data_ = true;
  int add_symbolic_dim_to_tensor_data_ = -1;
  int num_run_calls_ = 1;
  std::vector<Data> input_data_;
  std::vector<Data> output_data_;
  std::vector<size_t> initializer_index_;
  std::vector<std::function<void(onnxruntime::Node& node)>> add_attribute_funcs_;

  IOnnxRuntimeOpSchemaRegistryList custom_schema_registries_;
  std::vector<std::shared_ptr<CustomRegistry>> custom_session_registries_;
};

template <typename TException>
void ExpectThrow(OpTester& test, const std::string& error_msg) {
  try {
    test.Run();
    // should throw and not reach this
    EXPECT_TRUE(false) << "Expected Run() to throw";
  } catch (TException ex) {
    EXPECT_THAT(ex.what(), testing::HasSubstr(error_msg));
  }
}

void DebugTrap();

void Check(const OpTester::Data& expected_data, const Tensor& output_tensor, const std::string& provider_type);

// Only used for CUDA test since no toher kernel has float 16 support
#ifdef USE_CUDA
inline void ConvertFloatToMLFloat16(const float* f_datat, MLFloat16* h_data, int input_size) {
  auto in_vector = ConstEigenVectorMap<float>(f_datat, input_size);
  auto output_vector = EigenVectorMap<Eigen::half>(static_cast<Eigen::half*>(static_cast<void*>(h_data)), input_size);
  output_vector = in_vector.template cast<Eigen::half>();
}
#endif

inline void ConvertMLFloat16ToFloat(const MLFloat16* h_data, float* f_data, int input_size) {
  auto in_vector =
      ConstEigenVectorMap<Eigen::half>(static_cast<const Eigen::half*>(static_cast<const void*>(h_data)), input_size);
  auto output_vector = EigenVectorMap<float>(f_data, input_size);
  output_vector = in_vector.template cast<float>();
}

}  // namespace test
}  // namespace onnxruntime


#ifndef TEST_ALL
#define TEST_ALL
//#include <cblas.h>

#include "test_matrix_util.h"
#include "test_spn.h"

int test_all(int result);

#endif


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

#ifdef __cplusplus

#include <common/chksum.h>
#include <common/delete_copy.h>
#include <common/logging.h>
#include <common/moveable_ptr.h>
#include <common/utils.h> /* MiB, UNLIKELY */
#include <api/components.h>
#include <api/fabric_itf.h>
#include <api/kvstore_itf.h>
#include <api/registrar_memory_direct.h> /* Opaque_memory_region */
#include <sys/mman.h>

#include "mcas_config.h"
#include "memory_registered.h"
#include <gsl/pointers> /* not_null */
#include <cassert>
#include <cstring>      /* memset */
#include <memory>       /* unique_ptr */

namespace
{
  inline auto alloc_base(std::size_t len) -> gsl::not_null<void *>
  {
    auto b = ::aligned_alloc(MiB(2), len);
    if (b == nullptr) {
      throw std::bad_alloc();
    }
    ::memset(b, 0, len);
    return gsl::not_null<void *>(b);
  }
}

namespace mcas
{
template <class Memory>
class Buffer_manager : private common::log_source {

  static constexpr const char *_cname       = "Buffer_manager";

public:
  static constexpr size_t DEFAULT_BUFFER_COUNT = NUM_SHARD_BUFFERS;
  static constexpr size_t BUFFER_LEN           = MiB(2); /* corresponds to huge page see below */
  using memory_registered_t                    = memory_registered<Memory>;

  using memory_region_t = typename Memory::memory_region_t;

  struct iov_mem_lock
  {
  private:
    common::moveable_ptr<::iovec> _iov;
  public:
    iov_mem_lock(::iovec *iov_)
      : _iov(iov_)
    {
      ::madvise(_iov->iov_base, _iov->iov_len, MADV_HUGEPAGE);
      ::mlock(_iov->iov_base, _iov->iov_len);
    }
    iov_mem_lock(iov_mem_lock &&) noexcept = default;
    ~iov_mem_lock()
    {
      if ( _iov )
      {
        ::munlock(_iov->iov_base, _iov->iov_len);
      }
    }
  };

  /* Although client tried to hide it with a reinterpret_cast, buffer_base is
   used as a component::memory_region_t */

  struct buffer_base : public component::Registrar_memory_direct::Opaque_memory_region, private common::log_source
  {
  private:
    static constexpr const char *_cname = "buffer_base";
    void *_base;
    std::size_t _original_length;
    memory_registered_t _region;
    const unsigned magic;

  public:
    buffer_base(unsigned    debug_level_,
             Memory * transport_,
             void *      base_,
             size_t      length_
    )
      : common::log_source(debug_level_)
      , _base(base_)
      , _original_length(length_)
      , _region(memory_registered_t(debug_level_, transport_, base_, length_, 0, 0))
      , magic(0xC0FFEE)
    {
      if ( length_ <= 1 )
      {
        throw std::domain_error("buffer length too small");
      }
      CPLOG(3, "%s::%s %p transport %p region %p", _cname, __func__, common::p_fmt(this),
            common::p_fmt(transport()), common::p_fmt(region()));
    }

    void *get_desc() const { return _region.desc(); }

    DELETE_COPY(buffer_base);

  protected:
    ~buffer_base()
    {
      CPLOG(3, "%s::%s %p transport %p region %p", _cname, __func__, common::p_fmt(this),
            common::p_fmt(transport()), common::p_fmt(region()));
    }

  public:
    size_t original_length() const { return _original_length; }
    inline gsl::not_null<void *> base() const { return _base; }
    auto region() const { return _region.mr(); }
    Memory * transport() const { return _region.transport(); }

#if 0
    /* unused */
    inline bool check_magic() const { return magic == 0xC0FFEE; }
    unsigned int crc32() const { return common::chksum32(_base, _length); }
#endif
  };

  struct free_deleter
  {
    void operator()(void *v_) { ::free(v_); }
  };

  struct buffer_internal
    : private std::unique_ptr<void, free_deleter>
    , public buffer_base
  {
    ::iovec iov[2];
    void *desc[2];
    iov_mem_lock _ml;
    using completion_t = void (*)(void *, buffer_internal *);
    completion_t   completion_cb;
    void *         value_adjunct;
    inline void set_completion(completion_t completion_) { completion_cb = completion_; }
    void set_completion(completion_t completion_, void *value_adjunct_)
    {
      completion_cb = completion_;
      value_adjunct = value_adjunct_;
    }
    buffer_internal(unsigned debug_level_,
             Memory *         transport_,
             size_t              length_
    )
      : std::unique_ptr<void, free_deleter>(alloc_base(length_))
      , buffer_base(debug_level_, transport_, std::unique_ptr<void, free_deleter>::get(), length_)
      , iov{{this->base(), this->original_length()}, {nullptr, 0}}
      , desc{this->get_desc(), nullptr}
      , _ml(&this->iov[0])
      , completion_cb(nullptr)
      , value_adjunct(nullptr)
    {
    }

    DELETE_COPY(buffer_internal);

    void set_length(size_t s) { iov->iov_len = s; }

    inline void reset_length()
    {
      assert(this->original_length() > 1);
      this->iov[0].iov_len  = this->original_length();
      value_adjunct = nullptr;
      completion_cb = nullptr;
    }

    size_t length() const { return iov[0].iov_len; }

    unsigned int crc32() const { return common::chksum32(iov->iov_base, iov->iov_len); }
  };

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"  // missing initializers
  Buffer_manager(unsigned debug_level_,
                 Memory *transport,
                 size_t buffer_count = DEFAULT_BUFFER_COUNT)
    : common::log_source(debug_level_),
      _buffer_count(buffer_count),
      _transport(transport)
  {
    for (unsigned i = 0; i < _buffer_count; i++) {
      const auto len  = BUFFER_LEN;
      _buffers.emplace_back(std::make_unique<buffer_internal>(debug_level_, _transport, len));
      _free.push_back(_buffers.back().get());
    }
    PLOG("%s %p allocated %lu buffers", __func__, common::p_fmt(this), buffer_count);
  }
#pragma GCC diagnostic pop

  Buffer_manager(Buffer_manager &&) noexcept = default;

  ~Buffer_manager()
  {
  }

  using completion_t = void (*)(void *, buffer_internal *);

  gsl::not_null<buffer_internal *> allocate(completion_t completion_)
  {
    if (UNLIKELY(_free.empty())) throw Program_exception("Buffer_manager: no shard buffers remaining");
    gsl::not_null<buffer_internal *> iob = _free.back();
    _free.pop_back();
    CPLOG(3, "%s::%s %p (%lu free)", _cname, __func__, common::p_fmt(iob), _free.size());
    iob->reset_length();
    iob->set_completion(completion_);
    return iob;
  }

  void free(gsl::not_null<buffer_internal *> iob)
  {
    CPLOG(3, "%s::%s %p (%lu free)", _cname, __func__, common::p_fmt(iob), _free.size());
    iob->reset_length();
    iob->set_completion(nullptr);
    _free.push_back(iob);
  }

private:
  // static constexpr size_t buffer_len() { return BUFFER_LEN; }

  using pool_t = component::IKVStore::pool_t;
  using key_t  = std::uint64_t;

  const size_t                           _buffer_count;
  gsl::not_null<Memory *>             _transport;
  std::vector<std::unique_ptr<buffer_internal>> _buffers;
  std::vector<buffer_internal *>        _free;
};
}  // namespace mcas

#endif
#endif


#pragma once

#include <gsl.h>
#include <string>
#include <cstdarg>
#include <typeinfo>
#include <exception>

#include "HE_Assert.h"
#include "TMP_Helper.h"

// Utility function that calls the right variadic formatting functions and returns an RAII string
std::string CStringFormat(const char* sFormat, va_list args);
std::string CStringFormat(const char* sFormat, ...);

// Default to_string functions
using std::to_string;
using gsl::to_string;
std::string to_string(const std::exception& e);
std::string to_string(void* p);

inline std::string& to_string(std::string& s) { return s; }
inline std::string to_string(std::string&& s) { return s; }
inline const std::string& to_string(const std::string& s) { return s; }
inline std::string to_string(const char* s) { return{ s }; }

// Built-in type formats specifiers
// Can be used to format a built-in type to a string with a special format.
// Use HasFormatSpecifier<T> to test statically (ex: static_assert(...)) if a type supports
// a format specifier
// The format specifier is in the same format as printf (http://en.cppreference.com/w/cpp/io/c/fprintf),
// except that the conversion format specifier (aka the last letter[s]) can be ignored, in which case the
// default specifier for the type will be used (ex: d for int, u for unsigned, f for float, etc...)
// On top of that, for long sized types, only the conversion specifier without the argument type can be
// supplied. For example, for unsigned long, the format could be " .4o", which will be converted to
// " .4ol". Supplying " .4ol" itself would also work
std::string to_string(int val, const std::string& sFormat); // Defaults to %[sFormat]d
std::string to_string(unsigned int val, const std::string& sFormat); // Defaults to %[sFormat]u
std::string to_string(long val, const std::string& sFormat); // Defaults to %[sFormat]dl
std::string to_string(unsigned long val, const std::string& sFormat); // Defaults to %[sFormat]ul
std::string to_string(long long val, const std::string& sFormat); // Defaults to %[sFormat]dll
std::string to_string(unsigned long long val, const std::string& sFormat); // Defaults to %[sFormat]ull
std::string to_string(float val, const std::string& sFormat); // Defaults to %[sFormat]f
std::string to_string(double val, const std::string& sFormat); // Defaults to %[sFormat]f
std::string to_string(long double val, const std::string& sFormat); // Defaults to %[sFormat]fL
std::string to_string(void* val, const std::string& sFormat); // Defaults to %[sFormat]p

namespace HE
{
	namespace Private
	{
		template<class T>
		using try_format = decltype(to_string(std::declval<T>()));

		template<class T>
		using has_format = has_op < T, try_format >;

		template<class T>
		using try_format_specifier = decltype(to_string(std::declval<T>(), std::declval<std::string>()));

		template<class T>
		using has_format_specifier = has_op<T, try_format_specifier >;

		template< typename Arg >
		std::string FormatIthArgN(size_t i, size_t n, Arg&& arg)
		{
			ASSERT_MSG(i == n, "String format token number was higher than the number of arguments");
			return to_string(std::forward<Arg>(arg));
		}

		template< typename Arg1, typename... Args >
		std::string FormatIthArgN(size_t i, size_t n, Arg1&& arg, Args&&... args)
		{
			if (i == n)
			{
				return to_string(std::forward<Arg1>(arg));
			}
			else
			{
				return FormatIthArgN(i, n + 1, std::forward<Args>(args)...);
			}
		}

		template< typename Arg>
		auto FormatIthArgFN(size_t i, size_t n, const std::string& format, Arg&& arg)
			-> std::enable_if_t<has_format_specifier<Arg>::value, std::string>
		{
			ASSERT_MSG(i == n, "String format token number was higher than the number of arguments");
			return to_string(std::forward<Arg>(arg), format);
		}

		template< typename Arg >
		auto FormatIthArgFN(size_t i, size_t n, const std::string& format, Arg&& arg)
			-> std::enable_if_t<!has_format_specifier<Arg>::value, std::string>
		{
			ASSERT_MSG(false , "A format specifier was supplied with type "s + typeid(Arg).name() + " which that does not support it");
			return "";
		}

		template< typename Arg1, typename... Args >
		std::string FormatIthArgFN(size_t i, size_t n, const std::string& format, Arg1&& arg, Args&&... args)
		{
			if (i == n)
			{
				return FormatIthArgFN(i, n, format, std::forward<Arg1>(arg));
			}
			else
			{
				return FormatIthArgFN(i, n + 1, format, std::forward<Args>(args)...);
			}
		}

		template< typename... Args >
		std::string FormatIthArg(size_t i, Args&&... args)
		{
			return FormatIthArgN(i, 0, std::forward<Args>(args)...);
		}

		template< typename... Args >
		std::string FormatIthArgF(size_t i, const std::string& format, Args&&... args)
		{
			return FormatIthArgFN(i, 0, format, std::forward<Args>(args)...);
		}
	}

	// Can be used in statically evaluated context to test if the type can be formatted
	// to a string and formatted with a format specifier
	template< class... T >
	constexpr bool HasFormat()
	{
		return and_<Private::has_format<T>...>::value;
	}

	template< class... T >
	constexpr bool HasFormatSpecifier()
	{
		return and_<Private::has_format_specifier<T>...>::value;
	}

	// Form: Format(sFormat, args...) -> sFormatted
	// Formats the string according to format tokens and arguments
	// Format token have the format "{i}" or "{i:xxxx}", where i is a 0-based argument index to be
	// converted to string or the character '_', which means next argument (starting at 0)
	// "xxxx" is a format string to be passed to the argument
	//
	// In order to be featured in a format token, an argument must have a "to_string" function available
	// either in the global namespace or in the same scope as the type of the argument, which
	// will be found through ADL
	// To have a format specifier, the argument must have a "to_string" which takes a string as
	// second argument
	// You can test for both of these conditions statically with HasFormat<T>() and HasFormatSpecifier<T>()

    // Example: Format("{0:dd-mm-yyyy}", date) -> to_string(date, "dd-mm-yyyy")

	// Pre-condition: The biggest index in the format string must be smaller than
	// the number of arguments, and indices can't be negative
	inline std::string Format(const std::string& sFormat)
	{
		return sFormat;
	}

	template< typename... Args>
	std::string Format(const std::string& sFormat, Args&&... args)
	{
		static_assert(HasFormat<Args...>(), "An argument cannot be formatted (HasFormat returns false)");

		using namespace HE::Private;

		std::string sOutput;
		size_t nNextArg = 0;
		for (size_t i = 0; i < sFormat.size(); ++i)
		{
			// Find the next format token
			auto const posToken = sFormat.find_first_of('{', i);

			// Add to the output all the characters up until the next format token (or the end)
			// and process the format token if necessary
			if (posToken == std::string::npos)
			{
				sOutput += sFormat.substr(i);
				break;
			}
			// The sequence "\{" is not a token start, so we continue as if it was a normal character
			else if (posToken != 0 && sFormat[posToken - 1] == '\\')
			{
				sOutput += sFormat.substr(i, posToken - i);
				i = posToken;
				continue;
			}
			// Otherwise, we have a format token
			else
			{
				sOutput += sFormat.substr(i, posToken - i); // Add everything before the token

				auto const endToken = sFormat.find_first_of('}', posToken);
				i = endToken;
				ASSERT_MSG(endToken != std::string::npos, "Token error in string format \"" + sFormat + "\"");

				// Find the index token inside the whole token
				auto const sFormatToken = sFormat.substr(posToken + 1, (endToken - 1) - posToken);
				auto const nSpecifierSentinel = sFormatToken.find_first_of(':');
				auto const sIndexToken = [sFormatToken, nSpecifierSentinel]() {
					if (nSpecifierSentinel == std::string::npos)
					{
						return sFormatToken;
					}
					else
					{
						return sFormatToken.substr(0, nSpecifierSentinel);
					}
				}();

				// Find the index of the current arg for this token
				auto const argPos = [sIndexToken, nNextArg]() -> size_t {
					if (sIndexToken == "_")
					{
						return nNextArg;
					}
					else
					{
						return std::stoi(sIndexToken);
					}
				}();
				nNextArg = argPos + 1;

				// Add the formatted argument to the output
				if (nSpecifierSentinel == std::string::npos)
				{
					sOutput += FormatIthArg(argPos, std::forward<Args>(args)...);
				}
				else
				{
					auto const sSpecifierToken = sFormatToken.substr(nSpecifierSentinel + 1);
					sOutput += FormatIthArgF(argPos, sSpecifierToken, std::forward<Args>(args)...);
				}
			}
		}

		return sOutput;
	}

	// Thread-safe logging
	void Log(const char* psMsg) noexcept;
	inline void Log(const std::string& sMsg) noexcept { Log(sMsg.c_str()); }
	void LogError(const char* psMsg) noexcept;
	inline void LogError(const std::string& sMsg) noexcept { LogError(sMsg.c_str()); }

	template< typename... Args>
	void Log(const std::string& sFormat, Args&&... args)
	{
		Log(Format(sFormat, std::forward<Args>(args)...));
	}

	template< typename... Args>
	void LogError(const std::string& sFormat, Args&&... args)
	{
		LogError(Format(sFormat, std::forward<Args>(args)...));
	}

	// Constexpr-friendly string
	template<class Char = char>
	class basic_constexpr_string
	{
	public:
		template<size_t N>
		constexpr basic_constexpr_string(const char(&a)[N]) noexcept :
			m_pStr{ a }, m_nSize{ N - 1 } {}

		constexpr Char operator[](size_t n) const
		{
			return n < m_nSize ? m_pStr[n] : throw std::out_of_range("");
		}

		constexpr size_t size() const noexcept { return m_nSize; }

	private:
		const Char* const m_pStr;
		const size_t m_nSize;
	};

	using constexpr_string = basic_constexpr_string<char>;
	using constexpr_wstring = basic_constexpr_string<wchar_t>;
	using constexpr_u16string = basic_constexpr_string<char16_t>;
	using constexpr_u32string = basic_constexpr_string<char32_t>;
}