#pragma once
// support functions for TString & c-string handling...
// v1.17

#include <tchar.h>
#include <cstdlib>
#include <codecvt>

#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif

#include <concepts>

#include <gsl/gsl>

// determine whether _Ty is a Number type (excluding char / wchar)
template<class _Ty>
struct is_Numeric
	: std::integral_constant<bool,
	(std::is_arithmetic_v<_Ty> || std::is_same_v<_Ty, std::byte> || std::is_enum_v<_Ty>)
	&& !std::is_same_v<_Ty, wchar_t>
	&& !std::is_same_v<_Ty, char>
	&& !std::is_pointer_v<_Ty>
	>
{
};

// determine whether T is a Number type (excluding char / wchar), same as is_Numeric<T>::value
template <typename T>
constexpr bool is_Numeric_v = is_Numeric<T>::value;

namespace std
{
	inline string to_string(const wstring& wstr)
	{
		//string str;
		//str.assign(wstr.begin(), wstr.end());
		//return str;

		using convert_type = std::codecvt_utf8<wchar_t>;
		std::wstring_convert<convert_type, wchar_t> converter;

		//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
		return converter.to_bytes(wstr);
	}

	inline wstring to_wstring(const string& str)
	{
		wstring wstr;
		wstr.assign(str.begin(), str.end());
		return wstr;
	}

	inline string to_string(const std::byte& num)
	{
		return to_string(gsl::narrow_cast<uint8_t>(num));
	}

	inline wstring to_wstring(const std::byte& num)
	{
		return to_wstring(gsl::narrow_cast<uint8_t>(num));
	}
}

struct ci_char_traits
	: public std::char_traits<char>
{
	static char to_upper(char ch) noexcept
	{
		if (ch >= 'a' && ch <= 'z') return _toupper(ch);
		return ch;
	}
	static bool eq(char c1, char c2) noexcept
	{
		return to_upper(c1) == to_upper(c2);
	}
	static bool lt(char c1, char c2) noexcept
	{
		return to_upper(c1) < to_upper(c2);
	}
	static int compare(const char* s1, const char* s2, size_t n) noexcept
	{
		while (n-- != 0)
		{
			if (to_upper(*s1) < to_upper(*s2)) return -1;
			if (to_upper(*s1) > to_upper(*s2)) return 1;
			++s1; ++s2;
		}
		return 0;
	}
	static const char* find(const char* s, int n, char a) noexcept
	{
		if (s)
			for (auto const ua(to_upper(a)); (n != 0); ++s, --n)
			{
				if (to_upper(*s) == ua)
					return s;
			}
		return nullptr;
	}
};
struct ci_wchar_traits
	: public std::char_traits<wchar_t>
{
	static wchar_t to_upper(wchar_t ch) noexcept
	{
		if (ch >= L'a' && ch <= L'z') return _toupper(ch);
		return ch;
	}
	static bool eq(wchar_t c1, wchar_t c2) noexcept
	{
		return to_upper(c1) == to_upper(c2);
	}
	static bool lt(wchar_t c1, wchar_t c2) noexcept
	{
		return to_upper(c1) < to_upper(c2);
	}
	static int compare(const wchar_t* s1, const wchar_t* s2, size_t n) noexcept
	{
		while (n-- != 0)
		{
			if (to_upper(*s1) < to_upper(*s2)) return -1;
			if (to_upper(*s1) > to_upper(*s2)) return 1;
			++s1; ++s2;
		}
		return 0;
	}
	static const wchar_t* find(const wchar_t* s, int n, wchar_t a) noexcept
	{
		if (s)
			for (auto const ua(to_upper(a)); (n-- != 0); ++s)
			{
				if (to_upper(*s) == ua)
					return s;
			}
		return nullptr;
	}
};
using ci_stringview = std::basic_string_view<char, ci_char_traits>;
using ci_wstringview = std::basic_string_view<wchar_t, ci_wchar_traits>;

namespace details {
	template<class _Ty>
	struct is_pod
		: std::integral_constant<bool, std::is_standard_layout_v<_Ty>&& std::is_trivial_v<_Ty> >
	{
	};

	template <typename T>
	constexpr bool is_pod_v = is_pod<T>::value;

	template<typename T>
	concept IsPODText = std::is_same_v<std::remove_all_extents_t<std::remove_cv_t<std::remove_pointer_t<T>>>, char> || std::is_same_v<std::remove_all_extents_t<std::remove_cv_t<std::remove_pointer_t<T>>>, wchar_t>;

	template <class T>
	concept IsNumeric = is_Numeric_v<std::remove_cvref_t<T>>;

	template <class T>
	concept HasDataFunction = requires(T t)
	{
		t.data();
	};

	template <class T>
	concept HasChrFunction = requires(T t)
	{
		t.to_chr();
	};

	template <class T>
	concept HasSizeFunction = requires(T t)
	{
		static_cast<std::size_t>(t.size());
	};

	template <class T>
	concept HasDataSizeNoChr = HasDataFunction<T> && HasSizeFunction<T> && !HasChrFunction<T>;

	template <class T>
	concept HasDataSizeChr = HasDataFunction<T> && HasSizeFunction<T> && HasChrFunction<T>;

	constexpr WCHAR make_upper(const WCHAR c) noexcept
	{
		if (c >= L'a' && c <= L'z') return _toupper(c);
		return c;
	}
	constexpr char make_upper(const char c) noexcept
	{
		if (c >= 'a' && c <= 'z') return _toupper(c);
		return c;
	}
	constexpr bool CompareChar(const WCHAR c, const WCHAR other, const bool bCase) noexcept
	{
		return (bCase) ? c == other : make_upper(c) == make_upper(other);
	}
	constexpr bool CompareChar(const char c, const char other, const bool bCase) noexcept
	{
		return (bCase) ? c == other : make_upper(c) == make_upper(other);
	}

	template <typename Result, typename Format>
	Result& _ts_printf_do(Result& res, const Format& fmt)
	{
		res += fmt;
		return res;
	}

	template <typename Result, typename Format, typename Value, typename... Arguments>
	Result& _ts_printf_do(Result& res, const Format& fmt, const Value& val, Arguments&&... args)
	{
		auto i = 0U;
		auto bSkip = false;

		for (auto c = fmt[0]; c != decltype(c){'\0'}; c = fmt[++i])
		{
			if (!bSkip)
			{
				if (c == decltype(c){'\\'})
				{
					bSkip = true;
					continue;
				}
				else if (c == decltype(c){'%'})
				{
					if constexpr (is_Numeric_v<Value>)
					{
						if constexpr (std::is_same_v<std::string, std::remove_cv_t<Result>>)
							res += std::to_string(val);
						else if constexpr (std::is_same_v<std::wstring, std::remove_cv_t<Result>>)
							res += std::to_wstring(val);
						else
							res += val;	// assumes this type can handle adding numbers.
					}
					else
						res += val;

					if constexpr (std::is_array_v<Format> && details::is_pod_v<Format>)
						return _ts_printf_do(res, &fmt[0] + i + 1, args...);
					else
						return _ts_printf_do(res, fmt + i + 1, args...);
				}
			}
			else
				bSkip = false;
			res += c;
		}

		//// Ook: needs checked may need work
		//for (const auto &c: fmt)
		//{
		//	if (!bSkip)
		//	{
		//		if (c == decltype(c){'\\'})
		//		{
		//			bSkip = true;
		//			continue;
		//		}
		//		else if (c == decltype(c){'%'})
		//		{
		//			if constexpr (is_Numeric_v<Value>)
		//			{
		//				if constexpr (std::is_same_v<std::string, std::remove_cv_t<Result>>)
		//					res += std::to_string(val);
		//				else if constexpr (std::is_same_v<std::wstring, std::remove_cv_t<Result>>)
		//					res += std::to_wstring(val);
		//				else
		//					res += val;	// assumes this type can handle adding numbers.
		//			}
		//			else
		//				res += val;
		//			if constexpr (std::is_array_v<Format> && std::is_pod_v<Format>)
		//				return _ts_printf_do(res, &fmt[0] + i + 1, args...);
		//			else
		//				return _ts_printf_do(res, fmt + i + 1, args...);
		//		}
		//	}
		//	else
		//		bSkip = false;
		//	res += c;
		//}
		return res;
	}

	template<typename T, class TR = std::char_traits<T> >
	struct impl_strstr {
		using ptype = typename TR::char_type*;
		using cptype = typename const ptype;

		ptype operator()(ptype input, cptype find)
		{
			do {
				ptype p{}, q{};
				for (p = input, q = find; !TR::eq(*q, 0) && TR::eq(*p, *q); p++, q++) {}

				if (TR::eq(*q, 0))
					return input;

			} while (!TR::eq(*(input++), 0));
			return nullptr;
		}
	};

	//// Test if a string is empty, works for std::basic_string & TString objects
	//template <typename T>
	//std::enable_if_t<!std::is_pointer_v<T> && !std::is_pod_v<T> && std::is_member_function_pointer_v<decltype(&T::empty)>, bool> _ts_isEmpty(const T &str) noexcept
	//{
	//	return str.empty();
	//}
	//
	//// Test if a string is empty, works for C String char * or wchar_t *
	//template <typename T>
	//std::enable_if_t<std::is_pointer_v<T>, bool> _ts_isEmpty(const T &str) noexcept
	//{
	//	using value_type = std::remove_cv_t<std::remove_pointer_t<T> >;
	//
	//	static_assert(std::is_same_v<value_type, char> || std::is_same_v<value_type, wchar_t>, "Invalid Type used");
	//	return ((str == nullptr) || (str[0] == value_type()));
	//}
	//
	//// Test if a string is empty, works for C char or wchar_t
	//template <typename T>
	//std::enable_if_t<!std::is_pointer_v<T> && std::is_pod_v<T>, bool> _ts_isEmpty(const T &str) noexcept
	//{
	//	using value_type = std::remove_cv_t<T>;
	//
	//	static_assert(std::is_same_v<value_type, char> || std::is_same_v<value_type, wchar_t>, "Invalid Type used");
	//	return (str == value_type());
	//}

	template <typename T>
	bool _ts_isEmpty(const T& str) noexcept
	{
		if constexpr (std::is_pointer_v<T>)
		{
			// T is a pointer
			// Test if a string is empty, works for C String char * or wchar_t *
			using value_type = std::remove_cv_t<std::remove_pointer_t<T> >;

			static_assert(IsPODText<value_type>, "Invalid Type used");
			return ((str == nullptr) || (str[0] == value_type()));
		}
		else if constexpr (std::is_standard_layout_v<T> && std::is_trivial_v<T>) // same as old std::is_pod_v<T>
		{
			if constexpr (std::is_array_v<T>)
			{
				// T is NOT a pointer but IS POD and IS an array (char[3] ...)
				// Test if a string is empty, works for C char or wchar_t
				using value_type = std::remove_all_extents_t<std::remove_cv_t<T>>;

				static_assert(IsPODText<value_type>, "Invalid Type used");
				return (str[0] == value_type());
			}
			else {
				// T is NOT a pointer but IS POD and is NOT an array (single char ...)
				// Test if a string is empty, works for C char or wchar_t
				using value_type = std::remove_cv_t<T>;

				static_assert(IsPODText<value_type>, "Invalid Type used");
				return (str == value_type());
			}
		}
		else if constexpr (std::is_member_function_pointer_v<decltype(&T::empty)>)
		{
			// T is NOT a pointer and is NOT POD
			// Test if a container is empty, works for std::basic_string & TString objects or any container with an empty() member function.
			return str.empty();
		}
	}

	//// Get String length
	//template <typename T, typename size_type = std::size_t>
	//std::enable_if_t<std::is_pointer_v<T>, size_type> _ts_strlen(const T &str) noexcept
	//{
	//	using value_type = std::remove_cv_t<std::remove_pointer_t<T> >;
	//
	//	static_assert(std::is_same_v<value_type, char> || std::is_same_v<value_type, wchar_t>, "Invalid Type used");
	//	auto iLen = size_type();
	//	for (auto p = str; p && *p; ++p)
	//		++iLen;
	//	return iLen;
	//}
	//
	//template <typename T, typename size_type = std::size_t>
	//constexpr inline std::enable_if_t<!std::is_pointer_v<T> && std::is_same_v<std::remove_cv_t<T>, wchar_t>, size_type> _ts_strlen(const T &str) noexcept
	//{
	//	return (str == T() ? 0U : 1U);
	//}
	//
	//template <typename T, typename size_type = std::size_t>
	//constexpr inline std::enable_if_t<!std::is_pointer_v<T> && std::is_same_v<std::remove_cv_t<T>, char>, size_type> _ts_strlen(const T &str) noexcept
	//{
	//	return (str == T() ? 0U : 1U);
	//}
	//
	//template <typename T, typename size_type = std::size_t, std::size_t N>
	//constexpr inline size_type _ts_strlen(T const (&)[N]) noexcept
	//{
	//	return (N == 0U ? 0U : N - 1);
	//}
	//
	//template <typename T, typename size_type = T::size_type>
	//inline std::enable_if_t<!std::is_pointer_v<T> && std::is_member_function_pointer_v<decltype(&T::length)>, size_type> _ts_strlen(const T &str)
	//{
	//	return str.length();
	//}

	// Get String length
	template <typename T, IsNumeric size_type = std::size_t>
	constexpr size_type _ts_strlen(const T& str) noexcept
	{
		if constexpr (std::is_pointer_v<T>)
		{
			// T is a pointer
			//  return length of string...

			static_assert(IsPODText<T>, "Invalid Type used");
			auto iLen = size_type();
			for (auto p = str; p && *p; ++p)
				++iLen;
			return iLen;
		}
		else if constexpr (IsPODText<T>)
		{
			// T is POD, char or wchar_t
			// checks if char is zero or not
			return (str == T() ? 0U : 1U);
		}
		else if constexpr (std::is_member_function_pointer_v<decltype(&T::length)>)
		{
			// T has a member function length()
			return gsl::narrow_cast<size_type>(str.length());
		}
	}

	//template <typename T, typename size_type = std::size_t, std::size_t N>
	//constexpr inline size_type _ts_strlen(T const (&)[N]) noexcept
	//{
	//	// T is a fixed array
	//	return (N == 0U ? 0U : N - 1);
	//}

	template <IsPODText T, IsNumeric size_type = std::size_t, std::size_t N>
	constexpr inline size_type _ts_strlen(T const (&)[N]) noexcept
	{
		// T is a fixed array
		return (N == 0U ? 0U : N - 1);
	}

	template <typename T>
	struct _impl_strnlen {
	};
	template <>
	struct _impl_strnlen<char*> {
		size_t operator()(const char* const ptr, size_t length) noexcept
		{
			return strnlen(ptr, length);
		}
	};
	template <>
	struct _impl_strnlen<wchar_t*> {
		size_t operator()(const wchar_t* const ptr, size_t length) noexcept
		{
			return wcsnlen(ptr, length);
		}
	};
	template <>
	struct _impl_strnlen<char> {
		size_t operator()(const char& ptr, size_t length) noexcept
		{
			return (ptr == 0 || length == 0 ? 0U : 1U);
		}
	};
	template <>
	struct _impl_strnlen<wchar_t> {
		size_t operator()(const wchar_t& ptr, size_t length) noexcept
		{
			return (ptr == 0 || length == 0 ? 0U : 1U);
		}
	};

	template <typename T>
	struct _impl_strcpyn {
	};
	template <>
	struct _impl_strcpyn<char> {
		char* operator()(char* const pDest, const char* const pSrc, const size_t length) noexcept
		{
			//auto t = strncpy(pDest, pSrc, length);	// doesn't guarantee NULL termination!
			//if (t)
			//	t[length - 1] = 0;	// make sure it ends in NULL
			//return t;

			return lstrcpynA(pDest, pSrc, gsl::narrow_cast<int>(length));
		}
	};
	template <>
	struct _impl_strcpyn<wchar_t> {
		wchar_t* operator()(wchar_t* const pDest, const wchar_t* const pSrc, const size_t length) noexcept
		{
			//auto t = wcsncpy(pDest, pSrc, length); // doesn't guarantee NULL termination!
			//if (t)
			//	t[length - 1] = 0;	// make sure it ends in NULL
			//return t;

			return lstrcpynW(pDest, pSrc, gsl::narrow_cast<int>(length));
		}
	};

	template <typename T>
	struct _impl_strcpy {
	};
	template <>
	struct _impl_strcpy<char> {
		char* operator()(char* const pDest, const char* const pSrc) noexcept
		{
			return strcpy(pDest, pSrc);
		}
	};
	template <>
	struct _impl_strcpy<wchar_t> {
		wchar_t* operator()(wchar_t* const pDest, const wchar_t* const pSrc) noexcept
		{
			return wcscpy(pDest, pSrc);
		}
	};

	template <typename T>
	struct _impl_strncat {
	};
	template <>
	struct _impl_strncat<char> {
		char* operator()(char* const pDest, const char* const pSrc, const size_t length) noexcept
		{
			return strncat(pDest, pSrc, length);
		}
	};
	template <>
	struct _impl_strncat<wchar_t> {
		wchar_t* operator()(wchar_t* const pDest, const wchar_t* const pSrc, const size_t length) noexcept
		{
			return wcsncat(pDest, pSrc, length);
		}
	};

	template <typename T>
	struct _impl_strcat {
	};
	template <>
	struct _impl_strcat<char> {
		char* operator()(char* const pDest, const char* const pSrc) noexcept
		{
			return strcat(pDest, pSrc);
		}
	};
	template <>
	struct _impl_strcat<wchar_t> {
		wchar_t* operator()(wchar_t* const pDest, const wchar_t* const pSrc) noexcept
		{
			return wcscat(pDest, pSrc);
		}
	};

	template <typename T>
	struct _impl_strchr {
	};
	template <>
	struct _impl_strchr<char> {
		char* operator()(char* const pString, const char ch)
		{
			return strchr(pString, ch);
		}
	};
	template <>
	struct _impl_strchr<wchar_t> {
		wchar_t* operator()(wchar_t* const pString, const wchar_t ch)
		{
			return wcschr(pString, ch);
		}
	};

	template <typename T>
	struct _impl_strncmp {
	};
	template <>
	struct _impl_strncmp<char> {
		int operator()(const char* const pDest, const char* const pSrc, const size_t length) noexcept
		{
			return strncmp(pDest, pSrc, length);
		}
	};
	template <>
	struct _impl_strncmp<wchar_t> {
		int operator()(const wchar_t* const pDest, const wchar_t* const pSrc, const size_t length) noexcept
		{
			return wcsncmp(pDest, pSrc, length);
		}
	};

	template <typename T>
	struct _impl_strnicmp {
	};
	template <>
	struct _impl_strnicmp<char> {
		int operator()(const char* const pDest, const char* const pSrc, const size_t length) noexcept
		{
			return _strnicmp(pDest, pSrc, length);
		}
	};
	template <>
	struct _impl_strnicmp<wchar_t> {
		int operator()(const wchar_t* const pDest, const wchar_t* const pSrc, const size_t length) noexcept
		{
			return _wcsnicmp(pDest, pSrc, length);
		}
	};

	template <typename T>
	struct _impl_strcmp {
	};
	template <>
	struct _impl_strcmp<char> {
		int operator()(const char* const pDest, const char* const pSrc) noexcept
		{
			return strcmp(pDest, pSrc);
		}
	};
	template <>
	struct _impl_strcmp<wchar_t> {
		int operator()(const wchar_t* const pDest, const wchar_t* const pSrc) noexcept
		{
			return wcscmp(pDest, pSrc);
		}
	};

	template <typename T>
	struct _impl_stricmp {
	};
	template <>
	struct _impl_stricmp<char> {
		int operator()(const char* const pDest, const char* const pSrc) noexcept
		{
			return _stricmp(pDest, pSrc);
		}
	};
	template <>
	struct _impl_stricmp<wchar_t> {
		int operator()(const wchar_t* const pDest, const wchar_t* const pSrc) noexcept
		{
			return _wcsicmp(pDest, pSrc);
		}
	};

	template <typename T>
	struct _impl_ts_find {
	};
	template <>
	struct _impl_ts_find<char> {
		const char* operator()(const char* const str, const char& srch) noexcept
		{
			return strchr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<const char> {
		const char* operator()(const char* const str, const char& srch) noexcept
		{
			return strchr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<char*> {
		const char* operator()(const char* const str, const char* srch) noexcept
		{
			return strstr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<const char*> {
		const char* operator()(const char* const str, const char* srch) noexcept
		{
			return strstr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<wchar_t> {
		const wchar_t* operator()(const wchar_t* const str, const wchar_t& srch) noexcept
		{
			return wcschr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<const wchar_t> {
		const wchar_t* operator()(const wchar_t* const str, const wchar_t& srch) noexcept
		{
			return wcschr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<wchar_t*> {
		const wchar_t* operator()(const wchar_t* const str, const wchar_t* srch) noexcept
		{
			return wcsstr(str, srch);
		}
	};
	template <>
	struct _impl_ts_find<const wchar_t*> {
		const wchar_t* operator()(const wchar_t* const str, const wchar_t* srch) noexcept
		{
			return wcsstr(str, srch);
		}
	};

	template <typename T>
	struct _impl_vscprintf {
	};
	template <>
	struct _impl_vscprintf<char> {
		const int operator()(const char* const fmt, const va_list args) noexcept
		{
			return _vscprintf(fmt, args);
		}
	};
	template <>
	struct _impl_vscprintf<wchar_t> {
		const int operator()(const wchar_t* const fmt, const va_list args) noexcept
		{
			return _vscwprintf(fmt, args);
		}
	};

	template <typename T>
	struct _impl_vsprintf {
	};
	template <>
	struct _impl_vsprintf<char> {
		const int operator()(char* const buf, size_t nCount, const char* const fmt, const va_list args) noexcept
		{
			return vsnprintf(buf, nCount, fmt, args);
		}
	};
	template <>
	struct _impl_vsprintf<wchar_t> {
		const int operator()(wchar_t* const buf, size_t nCount, const wchar_t* const fmt, const va_list args) noexcept
		{
			return vswprintf(buf, nCount, fmt, args);
		}
	};

	template <typename T, typename Format, typename... Arguments>
	struct _impl_snprintf {
	};
	template <typename... Arguments>
	struct _impl_snprintf<char, char, Arguments...> {
		const int operator()(char* const buf, const size_t nCount, const char* const fmt, const Arguments&&... args) noexcept
		{
			return snprintf(buf, nCount, fmt, args...);
		}
	};
	template <typename... Arguments>
	struct _impl_snprintf<wchar_t, wchar_t, Arguments...> {
		const int operator()(wchar_t* const buf, const size_t nCount, const wchar_t* const fmt, const Arguments&&... args) noexcept
		{
			return _snwprintf(buf, nCount, fmt, args...);
		}
	};

	//template <typename T, typename... Arguments>
	//struct _impl_snprintf<T, typename T::value_type, Arguments...> {
	//	const std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::data)> && std::is_member_function_pointer_v<decltype(&T::size)>, int> operator()(T& buf, const typename T::value_type* const fmt, const Arguments&&... args) noexcept
	//	{
	//		if constexpr (std::is_same_v<char, T::value_type>)
	//			return snprintf(buf.data(), buf.size(), fmt, args...);
	//		else
	//			return _snwprintf(buf.data(), buf.size(), fmt, args...);
	//	}
	//};

	template <HasDataSizeNoChr T, typename... Arguments>
	struct _impl_snprintf<T, typename T::value_type, Arguments...> {
		const int operator()(T& buf, const typename T::value_type* const fmt, const Arguments&&... args) noexcept
		{
			if constexpr (std::is_same_v<char, T::value_type>)
				return snprintf(buf.data(), buf.size(), fmt, args...);
			else
				return _snwprintf(buf.data(), buf.size(), fmt, args...);
		}
	};
	template <HasDataSizeChr T, typename... Arguments>
	struct _impl_snprintf<T, typename T::value_type, Arguments...> {
		const int operator()(T& buf, const typename T::value_type* const fmt, const Arguments&&... args) noexcept
		{
			if constexpr (std::is_same_v<char, T::value_type>)
				return snprintf(buf.to_chr(), buf.size(), fmt, args...);
			else
				return _snwprintf(buf.to_chr(), buf.size(), fmt, args...);
		}
	};

	template <typename T>
	struct _impl_atoi {
	};
	template <>
	struct _impl_atoi<char> {
		auto operator()(const char* const buf) noexcept
		{
			return atoi(buf);
		}
	};
	template <>
	struct _impl_atoi<wchar_t> {
		auto operator()(const wchar_t* const buf) noexcept
		{
			return _wtoi(buf);
		}
	};

	template <typename T>
	struct _impl_atoi64 {
	};
	template <>
	struct _impl_atoi64<char> {
		auto operator()(const char* const buf) noexcept
		{
			return _atoi64(buf);
		}
	};
	template <>
	struct _impl_atoi64<wchar_t> {
		auto operator()(const wchar_t* const buf) noexcept
		{
			return _wtoi64(buf);
		}
	};

	template <typename T>
	struct _impl_atof {
	};
	template <>
	struct _impl_atof<char> {
		auto operator()(const char* const buf) noexcept
		{
			return atof(buf);
		}
	};
	template <>
	struct _impl_atof<wchar_t> {
		auto operator()(const wchar_t* const buf) noexcept
		{
			return _wtof(buf);
		}
	};

	template <typename T>
	struct _impl_itoa {
	};
	template <>
	struct _impl_itoa<char> {
		auto operator()(const int val, char* const buf, const int radix) noexcept
		{
			return _itoa(val, buf, radix);
		}
	};
	template <>
	struct _impl_itoa<wchar_t> {
		auto operator()(const int val, wchar_t* const buf, const int radix) noexcept
		{
			return _itow(val, buf, radix);
		}
	};

	template <typename T>
	struct _impl_strtoul {
	};
	template <>
	struct _impl_strtoul<char> {
		auto operator()(const char* const buf, char** endptr, int radx) noexcept
		{
			return strtoul(buf, endptr, radx);
		}
	};
	template <>
	struct _impl_strtoul<wchar_t> {
		auto operator()(const wchar_t* const buf, wchar_t** endptr, int radx) noexcept
		{
			return wcstoul(buf, endptr, radx);
		}
	};

	/// <summary>
	/// Convert a string representation of a binary number to an unsigned long
	/// </summary>
	/// <typeparam name="T">- char or wchar_t</typeparam>
	template <typename T>
	struct _impl_bstrtoul {
	};
	template <>
	struct _impl_bstrtoul<char> {
		auto operator()(const char* const buf) noexcept
		{
			return strtoul(buf, nullptr, 2);
		}
	};
	template <>
	struct _impl_bstrtoul<wchar_t> {
		auto operator()(const wchar_t* const buf) noexcept
		{
			return wcstoul(buf, nullptr, 2);
		}
	};
	template <typename T>
	struct _impl_strtok {
	};
	template <>
	struct _impl_strtok<char> {
		auto operator()(char* const buf, const char* delim, char** contex) noexcept
		{
			return strtok_s(buf, delim, contex);
		}
	};

	template <>
	struct _impl_strtok<wchar_t> {
		auto operator()(wchar_t* const buf, const wchar_t* delim, wchar_t** contex) noexcept
		{
			return wcstok_s(buf, delim, contex);
		}
	};
}

// Check string bounds, make sure dest is not within the source string & vice versa (this could be a possible reason for some strcpyn() fails we see)
template <details::IsPODText T>
constexpr bool isInBounds(const T* const sDest, const T* const sSrc, const std::size_t iLen) noexcept
{
	return (sSrc >= sDest && sSrc <= (sDest + iLen)) || (sDest >= sSrc && sDest <= (sSrc + iLen));
}

template <typename Result, typename Format, typename Value, typename... Arguments>
Result& _ts_sprintf(Result& res, const Format& fmt, const Value& val, Arguments&&... args)
{
	static_assert(details::IsPODText<Format>, "Format string must be char or wchar_t");
	res.clear();
	return details::_ts_printf_do(res, fmt, val, args...);
}

template <details::IsPODText T>
T* _ts_strstr(T* input, const std::remove_const_t<T>* find)
{
	return details::impl_strstr<T>()(input, find);
}

// Get String length
template <typename T>
inline auto _ts_strlen(const T& str) noexcept
{
	return details::_ts_strlen(str);
}

// Get String length (with buffer size limit)
template <details::IsPODText T>
inline size_t _ts_strnlen(const T& str, const size_t& nBufSize) noexcept
{
	return details::_impl_strnlen<T>()(str, nBufSize);
}

// Test if a string is empty, works for any object that has an empty() member function as well as C strings.
template <typename T>
inline bool _ts_isEmpty(const T& str) noexcept
{
	return details::_ts_isEmpty(str);
}

// Finds a string or character within a string & returns a pointer to it.
template <details::IsPODText HayStack, details::IsPODText Needle>
inline HayStack* _ts_find(HayStack* const str, Needle srch) noexcept
{
	return const_cast<HayStack*>(details::_impl_ts_find<Needle>()(str, srch));
}
template <details::IsPODText HayStack, details::IsPODText Needle>
inline const HayStack* _ts_find(const HayStack* const str, Needle srch) noexcept
{
	return details::_impl_ts_find<Needle>()(str, srch);
}

// Finds a string literal within a string & returns a pointer to it.
template <typename T, size_t N>
inline T* _ts_find(T* const str, const T(&srch)[N]) noexcept
{
	return const_cast<T*>(details::_impl_ts_find<std::add_pointer_t<T>>()(str, &srch[0]));
}

//// Finds a string or character within a string & returns a pointer to it.
//template <typename T, typename TStr>
//inline TStr *_ts_find(TStr *const str, T &srch)
//{
//	return const_cast<TStr *>(details::_impl_ts_find<std::remove_cv_t<T>>()(str, srch));
//}
//
//// Finds a string literal within a string & returns a pointer to it.
//template <typename T, typename TStr, size_t N>
//inline TStr *_ts_find(TStr *const str, const T (&srch)[N])
//{
//	return const_cast<TStr *>(details::_impl_ts_find<std::add_pointer_t<T>>()(str, &srch[0]));
//}

//template <typename T, typename TStr>
//inline const TStr *_ts_find(const TStr *const str, const T &srch)
//{
//	return details::_impl_ts_find<std::remove_cv_t<T>>()(str, srch);
//}
//
//template <typename T, typename TStr>
//inline TStr *_ts_find(TStr *const str, const T &srch)
//{
//	return const_cast<TStr *>(details::_impl_ts_find<std::remove_cv_t<T>>()(str, srch));
//}

template <details::IsPODText T>
T* _ts_strcpyn(T* const sDest, const T* const sSrc, const size_t iChars) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	//#if DCX_DEBUG_OUTPUT
	//	if ((sDest == nullptr) || (sSrc == nullptr) || isInBounds<std::remove_cv_t<T> >(sDest, sSrc, iChars))
	//		return nullptr;
	//#else
	if ((!sDest) || (!sSrc))
		return nullptr;
	//#endif

	return details::_impl_strcpyn<T>()(sDest, sSrc, iChars);
}

template <details::IsPODText T>
T* _ts_strcpy(T* const sDest, const T* const sSrc) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	if ((!sDest) || (!sSrc))
		return nullptr;

	return details::_impl_strcpy<T>()(sDest, sSrc);
}

template <details::IsPODText T>
T* _ts_strncat(T* const sDest, const T* const sSrc, const size_t iChars) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strncat<T>()(sDest, sSrc, iChars);
}

template <details::IsPODText T>
T* _ts_strcat(T* const sDest, const T* const sSrc) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strcat<T>()(sDest, sSrc);
}

template <details::IsPODText T>
T* _ts_strchr(T* const sDest, const T ch)
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strchr<T>()(sDest, ch);
}

template <details::IsPODText T>
int _ts_strncmp(const T* const sDest, const T* const sSrc, const size_t iChars) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strncmp<T>()(sDest, sSrc, iChars);
}

template <details::IsPODText T>
int _ts_strnicmp(const T* const sDest, const T* const sSrc, const size_t iChars) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strnicmp<T>()(sDest, sSrc, iChars);
}

template <details::IsPODText T>
int _ts_strcmp(const T* const sDest, const T* const sSrc) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strcmp<T>()(sDest, sSrc);
}

template <details::IsPODText T>
int _ts_stricmp(const T* const sDest, const T* const sSrc) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_stricmp<T>()(sDest, sSrc);
}

template <details::IsPODText T>
int _ts_vscprintf(_Printf_format_string_ const T* const _Format, va_list _ArgList) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_vscprintf<T>()(_Format, _ArgList);
}

template <details::IsPODText T>
int _ts_vsprintf(T* const buf, size_t nCount, _Printf_format_string_ const T* const fmt, const va_list args) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_vsprintf<T>()(buf, nCount, fmt, args);
}

template <details::IsPODText T, typename Format, typename... Arguments>
int _ts_snprintf(T* const buf, const size_t nCount, _Printf_format_string_ const Format* const fmt, Arguments&&... args) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");
	static_assert(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<Format>>, "Buffer & format must have same type.");

	return details::_impl_snprintf<T, Format, Arguments...>()(buf, nCount, fmt, std::forward<Arguments>(args)...);
}

template <typename T, details::IsPODText Format, typename... Arguments>
int _ts_snprintf(T& buf, _Printf_format_string_ const Format* const fmt, Arguments&&... args) noexcept
{
	static_assert(details::IsPODText<Format>, "Only char & wchar_t supported...");

	return details::_impl_snprintf<T, Format, Arguments...>()(buf, fmt, std::forward<Arguments>(args)...);
}

template <details::IsPODText T>
auto _ts_atoi(const T* const buf) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_atoi<T>()(buf);
}

template <details::IsPODText T>
auto _ts_atoi64(const T* const buf) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_atoi64<T>()(buf);
}

template <details::IsPODText T>
auto _ts_atof(const T* const buf) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_atof<T>()(buf);
}

template <details::IsPODText T>
auto _ts_itoa(const int val, T* const buf, const int radix) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_itoa<T>()(val, buf, radix);
}

template <details::IsPODText T>
auto _ts_strtoul(const T* const buf, T** endptr, int base) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strtoul<T>()(buf, endptr, base);
}

/// <summary>
/// Convert a string representation of a binary number to an unsigned long
/// </summary>
/// <param name="buf">- String representing a binary number.</param>
/// <returns>Binary number</returns>
template <details::IsPODText T>
auto _ts_bstrtoul(const T* const buf) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_bstrtoul<T>()(buf);
}

/// <summary>
/// same as strtok_s()
/// </summary>
/// <param name="buf"></param>
/// <param name="delim"></param>
/// <param name="contex"></param>
/// <returns></returns>
template <details::IsPODText T>
auto _ts_strtok(T* const buf, const T* delim, T** contex) noexcept
{
	static_assert(details::IsPODText<T>, "Only char & wchar_t supported...");

	return details::_impl_strtok<T>()(buf, delim, contex);
}

/// <summary>
/// Converts a number into a binary string representing that number (whole integers only)
/// </summary>
/// <typeparam name="Result">A string type object</typeparam>
/// <param name="a">- The number to convert.</param>
/// <returns>The string representation of the number.</returns>
template <details::IsNumeric T, class Result>
Result DecimalToBinaryString(const T& a)
{
	Result binary;
	constexpr auto sz = sizeof(a) * 8;
	std::remove_const_t<T> mask = 1;
	for (std::remove_const_t<decltype(sz)> i = 0; i < sz; ++i)
	{
		if ((mask & a))
			binary = '1' + binary;
		else
			binary = '0' + binary;
		mask <<= 1;
	}
	return binary;
}

/// <summary>
/// Replace all instances of 'rep' in 'str' with 'with'
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="RepThis"></typeparam>
/// <typeparam name="WithThis"></typeparam>
/// <param name="str">- string to search</param>
/// <param name="rep">- string to search for</param>
/// <param name="with">- replacement string</param>
/// <returns>The string it was passed with the changes made.</returns>
template <class T, class RepThis, class WithThis>
T& _ts_replace(T& str, RepThis rep, WithThis with)
{
	for (std::size_t wlen = _ts_strlen(with), rlen = _ts_strlen(rep), pos = 0U; ; pos += wlen)
	{
		pos = str.find(rep, pos);
		if (pos == T::npos)
			break;

		if constexpr (std::is_same_v<char, std::remove_cv_t<WithThis>> || std::is_same_v<wchar_t, std::remove_cv_t<WithThis>>)
		{
			str.replace(pos, rlen, 1, with);
		}
		else
		{
			str.replace(pos, rlen, with);
		}
	}
	return str;
}

//void ReplaceStringInPlace(std::string& subject, const std::string& search, const std::string& replace)
//{
//	size_t pos = 0;
//	while ((pos = subject.find(search, pos)) != std::string::npos)
//	{
//		subject.replace(pos, search.length(), replace);
//		pos += replace.length();
//	}
//}

/// <summary>
/// Replace all instances of each character in 'rep' in 'str' with 'with'
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="RepThis"></typeparam>
/// <typeparam name="WithThis"></typeparam>
/// <param name="str">- string to search</param>
/// <param name="rep">- string of characters to search for</param>
/// <param name="with">- replacement string</param>
/// <returns>The string it was passed with the changes made.</returns>
template <class T, class RepThis, class WithThis>
T& _ts_mreplace(T& str, RepThis rep, WithThis with)
{
	for (std::ptrdiff_t i = 0; rep[i] != 0; ++i)
		_ts_replace(str, rep[i], with);
	return str;
}

/// <summary>
/// Remove all instances of 'rep' within 'str'
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="RemThis"></typeparam>
/// <param name="str">- string to search.</param>
/// <param name="rep">- string to remove.</param>
/// <returns>The string it was passed with the changes made.</returns>
template <class T, class RemThis>
T& _ts_remove(T& str, RemThis rep)
{
	for (auto itEnd = str.end(), itStart = str.begin(); str.erase(std::find(itStart, itEnd, rep)) != itEnd; ) {}

	return str;
}

/// <summary>
/// Remove spaces from the front and back of the string.
/// </summary>
/// <typeparam name="T">A string type object</typeparam>
/// <param name="str">- The string to trim.</param>
/// <returns>A reference to the same string object it was passed.</returns>
template <class T>
T& _ts_trim(T& str)
{
	if (str.empty())
		return str;

	while (str.front() == _T(' '))
		str.erase(0, 1);

	while (str.back() == _T(' '))
		str.pop_back();

	return str;
}

/// <summary>
/// Copies the string then remove spaces from the front and back of the string.
/// </summary>
/// <typeparam name="T">A string type object</typeparam>
/// <param name="str">- The string to trim.</param>
/// <returns>A copy of the string object it was passed.</returns>
template <class T>
T _ts_trim_and_copy(const T& str)
{
	if (str.empty())
		return str;

	T res(str);
	while (res.front() == _T(' '))
		res.erase(0, 1);

	while (res.back() == _T(' '))
		res.pop_back();

	return res;
}

/// <summary>
/// Trim the string and remove double spaces.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str">- string to modify.</param>
/// <returns>The string it was passed with the changes made.</returns>
template <class T>
T& _ts_strip(T& str)
{
	//_ts_remove(str, _T('\0x02'));	//02
	//_ts_remove(str, _T('\0x0F'));	//15
	//_ts_remove(str, _T('\0x16'));	//22
	//_ts_remove(str, _T('\0x1D'));	//29
	_ts_replace(str, _T("  "), _T(' '));
	// NB: TODO: add ctrl-k removal for colour codes.
	return _ts_trim(str);
}

/// <summary>
/// Change a string to uppercase.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str">- string to modify.</param>
/// <returns>The string it was passed with the changes made.</returns>
template <class T>
T& _ts_toupper(T& str)
{
	for (auto& a : str)
		a = details::make_upper(a);
	return str;
}

template <details::IsPODText T>
T _ts_toupper_c(const T& c) noexcept
{
	return details::make_upper(c);
}

template <details::IsPODText T>
bool _ts_isupper(const T& c) noexcept
{
	if constexpr (std::is_same_v<wchar_t, T>)
		return (iswupper(c) != 0);
	else
		return (isupper(c) != 0);
}

/// <summary>
/// Convert a string to a number.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="Input"></typeparam>
/// <param name="str">- string to convert.</param>
/// <returns>The number contained in the string.</returns>
template <class T, class Input>
T _ts_to_(const Input& str)
{
	static_assert(is_Numeric_v<T>, "Type T must be (int, long, float, double, ....)");

	std::basic_istringstream<TCHAR> ss(str);	// makes copy of string :(
	T result{};
	return ss >> result ? result : T();
}

#define TSTRING_WILDT 0
#define TSTRING_WILDA 0
#define TSTRING_WILDE 0
#define TSTRING_WILDW 0

//template <class T>
//constexpr auto getWildType(T s) noexcept
//{
//	if constexpr (details::is_pod_v<T>)
//		return typename std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<T>>>;
//	else
//		return typename std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<T::value_type>>>;
//}
//
//template <typename TameString, typename WildString>
//[[gsl::suppress(bounds)]] bool _ts_WildcardMatch(const TameString& pszString, const WildString& pszMatch, const bool bCase = false)
//{
//	//if ((!pszMatch) || (!pszString))
//	//	return false;
//
//	if (_ts_isEmpty(pszMatch) || _ts_isEmpty(pszString))
//		return false;
//
//	ptrdiff_t MatchPlaceholder = 0;
//	ptrdiff_t StringPlaceholder = 0;
//	ptrdiff_t iWildOffset = 0;
//	ptrdiff_t iTameOffset = 0;
//
//	//using _WildString = typename std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<WildString::value_type>>>;
//	using _WildString = typename std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<WildString>>>;
//	//auto a = getWildType(pszMatch);
//
//	constexpr _WildString _zero{};
//	constexpr _WildString _star = static_cast<_WildString>('*');
//	constexpr _WildString _question = static_cast<_WildString>('?');
//	constexpr _WildString _tilda = static_cast<_WildString>('~');
//	constexpr _WildString _space = static_cast<_WildString>(' ');
//	constexpr _WildString _power = static_cast<_WildString>('^');
//	constexpr _WildString _hash = static_cast<_WildString>('#');
//	constexpr _WildString _slash = static_cast<_WildString>('\\');
//	constexpr _WildString _tab = static_cast<_WildString>('\t');
//	constexpr _WildString _n = static_cast<_WildString>('\n');
//	constexpr _WildString _r = static_cast<_WildString>('\r');
//
//	while (pszString[iTameOffset])
//	{
//		if (pszMatch[iWildOffset] == _star)
//		{
//			if (pszMatch[++iWildOffset] == _zero)
//				return true;
//			MatchPlaceholder = iWildOffset;
//			StringPlaceholder = iTameOffset + 1;
//		}
//#if TSTRING_WILDT
//		else if (pszMatch[iWildOffset] == _tilda && pszString[iTameOffset] == _space)
//		{
//			++iWildOffset;
//			while (pszString[iTameOffset] == _space)
//				++iTameOffset;
//		}
//#endif
//#if TSTRING_WILDA
//		else if (pszMatch[iWildOffset] == _power)
//		{
//			++iWildOffset;
//			if (details::CompareChar(pszMatch[iWildOffset], pszString[iTameOffset], bCase))
//				++iTameOffset;
//			++iWildOffset;
//		}
//#endif
//#if TSTRING_WILDE
//		else if (pszMatch[iWildOffset] == _slash)
//		{
//			// any character following a '\' is taken as a literal character.
//			++iWildOffset;
//			if (!details::CompareChar(pszMatch[iWildOffset], pszString[iTameOffset], bCase))
//				return false;
//			++iTameOffset;
//		}
//#endif
//#if TSTRING_WILDW
//		else if (pszMatch[iWildOffset] == _hash)
//		{
//			++iWildOffset;
//			while (pszString[iTameOffset] && (pszString[iTameOffset] != _space || pszString[iTameOffset] != _tab || pszString[iTameOffset] != _n || pszString[iTameOffset] != _r))
//				++iTameOffset;
//		}
//#endif
//		//else if (pszMatch[iWildOffset] == TEXT('?') || _toupper(pszMatch[iWildOffset]) == _toupper(pszString[iTameOffset]))
//		else if (pszMatch[iWildOffset] == _question || details::CompareChar(pszMatch[iWildOffset], pszString[iTameOffset], bCase))
//		{
//			++iWildOffset;
//			++iTameOffset;
//		}
//		else if (StringPlaceholder == 0)
//			return false;
//		else
//		{
//			iWildOffset = MatchPlaceholder;
//			iTameOffset = StringPlaceholder++;
//		}
//	}
//
//	while (pszMatch[iWildOffset] == _star)
//		++iWildOffset;
//
//	return !pszMatch[iWildOffset];
//}

/// <summary>
/// Internal wildcard match function.
/// </summary>
/// <typeparam name="_Wild"></typeparam>
/// <typeparam name="TameString"></typeparam>
/// <typeparam name="WildString"></typeparam>
/// <param name="pszString"></param>
/// <param name="pszMatch"></param>
/// <param name="bCase"></param>
/// <returns></returns>
template <class _Wild, typename TameString, typename WildString>
GSL_SUPPRESS(bounds.4) bool _ts_InnerWildcardMatch(const TameString& pszString, const WildString& pszMatch, const bool bCase) noexcept(std::is_nothrow_move_assignable_v<WildString>)
{
	ptrdiff_t MatchPlaceholder = 0;
	ptrdiff_t StringPlaceholder = 0;
	ptrdiff_t iWildOffset = 0;
	ptrdiff_t iTameOffset = 0;

	constexpr _Wild _zero{};
	constexpr _Wild _star = static_cast<_Wild>('*');
	constexpr _Wild _question = static_cast<_Wild>('?');
	constexpr _Wild _tilda = static_cast<_Wild>('~');
	constexpr _Wild _space = static_cast<_Wild>(' ');
	constexpr _Wild _power = static_cast<_Wild>('^');
	constexpr _Wild _hash = static_cast<_Wild>('#');
	constexpr _Wild _slash = static_cast<_Wild>('\\');
	constexpr _Wild _tab = static_cast<_Wild>('\t');
	constexpr _Wild _n = static_cast<_Wild>('\n');
	constexpr _Wild _r = static_cast<_Wild>('\r');

	while (pszString[iTameOffset])
	{
		if (pszMatch[iWildOffset] == _star)
		{
			if (pszMatch[++iWildOffset] == _zero)
				return true;
			MatchPlaceholder = iWildOffset;
			StringPlaceholder = iTameOffset + 1;
		}
#if TSTRING_WILDT
		else if (pszMatch[iWildOffset] == _tilda && pszString[iTameOffset] == _space)
		{
			++iWildOffset;
			while (pszString[iTameOffset] == _space)
				++iTameOffset;
		}
#endif
#if TSTRING_WILDA
		else if (pszMatch[iWildOffset] == _power)
		{
			++iWildOffset;
			if (details::CompareChar(pszMatch[iWildOffset], pszString[iTameOffset], bCase))
				++iTameOffset;
			++iWildOffset;
		}
#endif
#if TSTRING_WILDE
		else if (pszMatch[iWildOffset] == _slash)
		{
			// any character following a '\' is taken as a literal character.
			++iWildOffset;
			if (!details::CompareChar(pszMatch[iWildOffset], pszString[iTameOffset], bCase))
				return false;
			++iTameOffset;
		}
#endif
#if TSTRING_WILDW
		else if (pszMatch[iWildOffset] == _hash)
		{
			++iWildOffset;
			while (pszString[iTameOffset] && (pszString[iTameOffset] != _space || pszString[iTameOffset] != _tab || pszString[iTameOffset] != _n || pszString[iTameOffset] != _r))
				++iTameOffset;
		}
#endif
		//else if (pszMatch[iWildOffset] == _question || _toupper(pszMatch[iWildOffset]) == _toupper(pszString[iTameOffset]))
		else if (pszMatch[iWildOffset] == _question || details::CompareChar(pszMatch[iWildOffset], pszString[iTameOffset], bCase))
		{
			++iWildOffset;
			++iTameOffset;
		}
		else if (StringPlaceholder == 0)
			return false;
		else
		{
			iWildOffset = MatchPlaceholder;
			iTameOffset = StringPlaceholder++;
		}
	}

	while (pszMatch[iWildOffset] == _star)
		++iWildOffset;

	return !pszMatch[iWildOffset];
}

/// <summary>
/// Wildcard match
/// </summary>
/// <typeparam name="TameString"></typeparam>
/// <typeparam name="WildString"></typeparam>
/// <param name="pszString">- string to search</param>
/// <param name="pszMatch">- the wildcard string to search for.</param>
/// <param name="bCase">- does case matter</param>
/// <returns>did match succeed true/false</returns>
template <typename TameString, typename WildString>
GSL_SUPPRESS(bounds) bool _ts_WildcardMatch(const TameString& pszString, const WildString& pszMatch, const bool bCase = false) noexcept(std::is_nothrow_move_assignable_v<WildString>)
{
	if (_ts_isEmpty(pszMatch) || _ts_isEmpty(pszString))
		return false;

	if constexpr (details::is_pod_v<std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<WildString>>>>)
		return _ts_InnerWildcardMatch<std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<WildString>>>>(pszString, pszMatch, bCase);
	else
		return _ts_InnerWildcardMatch<std::remove_pointer_t<std::remove_cv_t<std::remove_all_extents_t<WildString::value_type>>>>(pszString, pszMatch, bCase);
}

template <typename T> T* _ts_AddMem(std::vector<std::shared_ptr<std::vector<char>>>& mem, std::size_t sz = sizeof(T))
{
	std::shared_ptr<std::vector<char>> x = std::make_shared<std::vector<char>>();
	x->resize(sz);
	mem.push_back(x);
	T* d = (T*)mem[mem.size() - 1].get()->data();
	return d;
}

/// <summary>
/// Sort a vector
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str">- The vector to sort.</param>
/// <returns>A sorted copy of the input.</returns>
template <class T>
std::vector<T> _ts_SortString(const std::vector<T>& str)
{
	std::vector<T> out(str);

	std::sort(out.begin(), out.end(), std::less<T>);

	return out;
}


#ifndef _PYGSL_SOLVER_H_
#define _PYGSL_SOLVER_H_ 1
#include <pygsl/intern.h>
#include <pygsl/block_helpers.h>
/* Not directly needed here, but provides a lot of convienience functions */
#include <pygsl/error_helpers.h>
#include <gsl/gsl_math.h>
#include <setjmp.h>
/*
 * Many functions are "just" accessor methods. These different methods are
 * listed here.
 *
 * Convention: they all end with "m_t" short for method type. This emphasises
 * that this type is pointer to a method.
 */
typedef int (*int_m_t)(void *);
typedef size_t (*size_t_m_t)(void *);
typedef void (*void_m_t)(void *);
typedef void *(*void_a_t)(const void *);
typedef void *(*void_an_t)(const void *, size_t n);
typedef void *(*void_anp_t)(const void *, size_t n, size_t p);
typedef const char * (*name_m_t)(void *);
typedef double  (*double_m_t)(void *);
typedef gsl_vector *(*ret_vec)(void *);
typedef int (*set_m_t)(void *, void *, const gsl_vector *);
typedef int (*set_m_d_t) (void *, gsl_function *, double);
typedef int (*set_m_ddd_t) (void *, gsl_function *, double, double, double);
typedef int (*int_f_vd_t)(const gsl_vector *, double);
typedef int (*int_f_vvdd_t)(const gsl_vector *, const gsl_vector *, double, double);

struct _PyGSLSolverObject;

/*
 * GSL Methods which are implemented as C functions. The specifiy pointer to
 * the solver struct are passed as (void *) pointers.
 */
struct _GSLMethods{
     /* Method to be called to free the GSL solver */
     void_m_t free;
     /*
      * Some solvers provide a restart method. If not available set it to NULL.
      */
     void_m_t restart;
     /* returns a string with the name of the solver */
     name_m_t name;
     /* takes one more step towards the solution */
     int_m_t iterate;
};

struct _SolverStatic{
     struct _GSLMethods cmethods;
     /* How many callbacks will be used? */
     int n_cbs;
     /* Additional methods not provided by the basis solver. */
     PyMethodDef *pymethods;
     /* Describes the type of the solver. e. g. F-Minimizer */
     const char * type_name;
};

struct pygsl_array_cache{
     double * data;
     PyArrayObject * ref;
};
#define PyGSL_SOLVER_NCBS_MAX 4
#define PyGSL_SOLVER_PB_ND_MAX 2
#define PyGSL_SOLVER_N_ARRAYS 10

struct _PyGSLSolverObject{
     PyObject_HEAD
     /*
      *	Some solvers do not propagate errors. Here I use longjmp if an error
      * is raised by the evaluator.
      */
     jmp_buf buffer;
     /*
      * Array generation is a vital part of the callback process. To increase
      * the calculation speed, I want to store them here, thus I can reuse them
      * if not stored by the user for later evaluation.
      */
     struct pygsl_array_cache *cache;
     /*
      * The Python callback methods.
      */
     PyObject* cbs[PyGSL_SOLVER_NCBS_MAX];
     /*
      * Additional arguments passed to the callbacks
      */
     PyObject* args;
     /*
      * The solver itself.
      */
     void * solver;
     /*
      * The space needed to store the variables for the C functions ....
      */
     void * c_sys;
     /*
      * The dimensionality of the problem. Typically one or two numbers ...
      */
     int problem_dimensions[PyGSL_SOLVER_PB_ND_MAX];
     /*
      * The methods of the solver.
      */
     const struct _SolverStatic* mstatic;
     /* before one can iterate the solver the method set() must be called!*/
     int set_called;
     /* Used as a flag if the jmp_buf is set */
     int isset;
};
typedef struct _PyGSLSolverObject PyGSL_solver;

typedef struct{
     const void * type;
     void* alloc;
     const struct _SolverStatic* mstatic;
} solver_alloc_struct;

#ifndef _PyGSL_SOLVER_API_MODULE
#define PyGSL_SOLVER_API_EXTERN extern
#else
#define PyGSL_SOLVER_API_EXTERN static
#endif

/*
 *  Initalises a solver
 *
 *  The internal structure is set up and the
 *  nd: number of dimensions
 *     0 ... zero dimensional e.g. minimize, root
 *     1 ... one  dimensional e.g. multimin
 *     2 ... two  dimensional e.g. multifit_nlin
 *
 *     3 ... only initalise the structure
 *
 */
PyGSL_SOLVER_API_EXTERN  PyObject *
PyGSL_solver_dn_init(PyObject *self, PyObject *args, const solver_alloc_struct * alloc, int nd);

/*
 * Accessor methods.
 *
 * These methods allow to access parameters of the C structure using the access
 * methods _m_t func
 */
PyGSL_SOLVER_API_EXTERN PyObject*
PyGSL_solver_ret_double(PyGSL_solver *self, PyObject *args, double_m_t func);

PyGSL_SOLVER_API_EXTERN PyObject*
PyGSL_solver_ret_int(PyGSL_solver *self, PyObject *args, int_m_t func);

PyGSL_SOLVER_API_EXTERN PyObject*
PyGSL_solver_ret_size_t(PyGSL_solver *self, PyObject *args, size_t_m_t func);

PyGSL_SOLVER_API_EXTERN PyObject*
PyGSL_solver_ret_vec(PyGSL_solver *self, PyObject *args,  ret_vec func);

/*
 * evaluates a C function taking an vector and a double as input and returning a status.
 */

#define PyGSL_CALLABLE_CHECK(ob, name) \
(PyCallable_Check(ob) ? GSL_SUCCESS : PyGSL_Callable_Check(ob, name))

PyGSL_SOLVER_API_EXTERN int
PyGSL_Callable_Check(PyObject *f, const char * myname);

PyGSL_SOLVER_API_EXTERN int
PyGSL_function_wrap_On_O(const gsl_vector * x, PyObject *callback,
			 PyObject *arguments, double *result1,
			 gsl_vector *result2, int n, const char * c_func_name);

PyGSL_SOLVER_API_EXTERN int
PyGSL_function_wrap_OnOn_On(const gsl_vector *x, const gsl_vector *v, gsl_vector *hv, PyObject *callback,
			    PyObject *arguments, int n, const char *c_func_name);

PyGSL_SOLVER_API_EXTERN int
PyGSL_function_wrap_Op_On_Opn(const gsl_vector *x, gsl_vector *f1, gsl_matrix *f2, PyObject *callback,
			    PyObject *arguments, int n, int p, const char *c_func_name);

PyGSL_SOLVER_API_EXTERN int
PyGSL_function_wrap_Op_On(const gsl_vector * x, gsl_vector *f, PyObject *callback,
			  PyObject * arguments, int n, int p, const char *c_func_name);

PyGSL_SOLVER_API_EXTERN int
PyGSL_function_wrap_Op_Opn(const gsl_vector * x, gsl_matrix *f, PyObject *callback,
			   PyObject *arguments, int n, int p, const char * c_func_name);

/*
 * evaluates a C function taking an vector and a double as input and returning a status.
 */
PyGSL_SOLVER_API_EXTERN PyObject*
PyGSL_solver_vd_i(PyObject * self, PyObject *args, int_f_vd_t func);

PyGSL_SOLVER_API_EXTERN PyObject *
PyGSL_solver_vvdd_i(PyObject * self, PyObject * args, int_f_vvdd_t func);

struct pygsl_solver_n_set{
     int is_fdf;
     void *c_sys;
     set_m_t set;
};

PyGSL_SOLVER_API_EXTERN PyObject *
PyGSL_solver_n_set(PyGSL_solver *self, PyObject *pyargs, PyObject *kw,
		   const struct pygsl_solver_n_set * info);

/*
 *
 */
PyGSL_SOLVER_API_EXTERN int
PyGSL_solver_func_set(PyGSL_solver *self, PyObject *args, PyObject *f,
		       PyObject *df, PyObject *fdf);

PyGSL_SOLVER_API_EXTERN PyObject*
PyGSL_solver_set_f(PyGSL_solver *self, PyObject *pyargs, PyObject *kw,
		   void *fptr, int isfdf);

#define _GET(name, cast, func) \
PyObject * PyGSL_ ## name(PyGSL_solver *self, PyObject *args) \
{ \
    return PyGSL_solver_ ## func(self, args, (cast) gsl_ ## name); \
}

#define GETDOUBLE(name) _GET(name, double_m_t, ret_double)
#define GETSIZET(name)  _GET(name, size_t_m_t, ret_size_t)
#define GETINT(name)    _GET(name, int_m_t,    ret_int)
#define GETVEC(name)    _GET(name, ret_vec,    ret_vec)

/*
 * Get or set a double ....
 */
enum PyGSL_GETSET_typemode {
     PyGSL_MODE_DOUBLE = 0,
     PyGSL_MODE_INT,
     PyGSL_MODE_SIZE_T
};

PyGSL_SOLVER_API_EXTERN PyObject *
PyGSL_solver_GetSet(PyObject *self, PyObject *args, void * address, enum PyGSL_GETSET_typemode mode);

#ifndef _PyGSL_SOLVER_API_MODULE

#define PyGSL_function_wrap_Op_On \
(* (int (*)(const gsl_vector *, gsl_vector *, PyObject *, PyObject *, int, int, const char *))\
 PyGSL_API[PyGSL_function_wrap_Op_On_NUM])

#define PyGSL_function_wrap_On_O \
(* (int (*)(const gsl_vector *, PyObject *, PyObject *, double *, gsl_vector *, int, const char *))\
 PyGSL_API[PyGSL_function_wrap_On_O_NUM])

#define PyGSL_function_wrap_OnOn_On \
(* (int (*)(const gsl_vector *, const gsl_vector *, gsl_vector *, PyObject *,  PyObject *,int, const char *))\
 PyGSL_API[PyGSL_function_wrap_OnOn_On_NUM])

#define PyGSL_function_wrap_Op_On_Opn \
(* (int (*)(const gsl_vector *, gsl_vector *, gsl_matrix *, PyObject *, PyObject *, int, int, const char *))\
 PyGSL_API[PyGSL_function_wrap_Op_On_Opn_NUM])

#define PyGSL_function_wrap_Op_Opn \
(* (int (*)(const gsl_vector *, gsl_matrix *, PyObject *, PyObject *, int, int, const char *))\
 PyGSL_API[PyGSL_function_wrap_Op_Opn_NUM])

#define PyGSL_solver_ret_int \
(*(PyObject *  (*) (PyGSL_solver *, PyObject *, int_m_t func))    PyGSL_API[PyGSL_solver_ret_int_NUM])

#define PyGSL_solver_ret_double \
(*(PyObject *  (*) (PyGSL_solver *, PyObject *, double_m_t func)) PyGSL_API[PyGSL_solver_ret_double_NUM])

#define PyGSL_solver_ret_size_t \
(*(PyObject *  (*) (PyGSL_solver *, PyObject *, size_t_m_t func)) PyGSL_API[PyGSL_solver_ret_size_t_NUM])

#define PyGSL_solver_ret_vec \
(*(PyObject *  (*) (PyGSL_solver *, PyObject *, ret_vec func))    PyGSL_API[PyGSL_solver_ret_vec_NUM])

#define  PyGSL_solver_dn_init \
(*(PyObject * (*)(PyObject *, PyObject *, const solver_alloc_struct *, int))PyGSL_API[PyGSL_solver_dn_init_NUM])

#define PyGSL_Callable_Check \
(*(int (*)(PyObject *, const char *)) PyGSL_API[PyGSL_Callable_Check_NUM])

#define PyGSL_solver_vd_i \
(*(PyObject * (*) (PyObject *, PyObject *, int_f_vd_t)) PyGSL_API[PyGSL_solver_vd_i_NUM])

#define PyGSL_solver_vvdd_i \
(*(PyObject * (*) (PyObject *, PyObject *, int_f_vvdd_t)) PyGSL_API[PyGSL_solver_vvdd_i_NUM])

#define PyGSL_solver_func_set \
(*(int (*)(PyGSL_solver *, PyObject *, PyObject *, PyObject *, PyObject *)) PyGSL_API[PyGSL_solver_func_set_NUM])

#define PyGSL_solver_n_set \
(*(PyObject * (*)(PyGSL_solver *, PyObject *, PyObject *,  const struct pygsl_solver_n_set *)) PyGSL_API[PyGSL_solver_n_set_NUM])

#define PyGSL_solver_set_f \
(* (PyObject * (*)(PyGSL_solver *, PyObject *, PyObject *, void *, int )) PyGSL_API[PyGSL_solver_set_f_NUM])

#define PyGSL_solver_GetSet \
(* (PyObject * (*) (PyObject *, PyObject *, void *, enum PyGSL_GETSET_typemode mode)) PyGSL_API[PyGSL_solver_getset_NUM])

#define PyGSL_solver_check(ob) (((ob)->ob_type) == (PyGSL_API[PyGSL_solver_type_NUM]))

#define import_pygsl_solver() \
{ \
   init_pygsl(); \
   if (PyImport_ImportModule("pygsl.testing.solver") != NULL) { \
          ;\
   } else { \
        fprintf(stderr, "failed to import pygsl solver!!\n"); \
   } \
}

#else  /* _PyGSL_API_MODULE */
#define PyGSL_solver_check(ob) ((ob)->ob_type == &PyGSL_solver_pytype)
#endif /* _PyGSL_API_MODULE */

#endif /* _PYGSL_SOLVER_H_ */


#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_math.h>

#include "allvars.h"
#include "proto.h"

#ifdef PEDANTIC_MEMORY_HANDLER

#define MAXBLOCKS 5000

#ifdef PEDANTIC_MEMORY_CEILING
static size_t TotBytes;
static void *Base;
#endif

static unsigned long Nblocks;
static double Highmark;

static void **Table;
static size_t *BlockSize;

void mymalloc_init(void)
{
#ifdef PEDANTIC_MEMORY_CEILING
  size_t n;
#endif

  BlockSize = (size_t *) malloc(MAXBLOCKS * sizeof(size_t));
  Table = (void **) malloc(MAXBLOCKS * sizeof(void *));

#ifdef PEDANTIC_MEMORY_CEILING
  n = PEDANTIC_MEMORY_CEILING * 1024.0 * 1024.0;

  if(!(Base = malloc(n)))
    {
      printf("Failed to allocate memory for `Base' (%d Mbytes).\n", (int) PEDANTIC_MEMORY_CEILING);
      endrun(122);
    }

  TotBytes = FreeBytes = n;
#endif

  AllocatedBytes = 0;
  Nblocks = 0;
  Highmark = 0;
}

void *mymalloc(size_t n)
{
  if((n % 8) > 0)
    n = (n / 8 + 1) * 8;

  if(n < 8)
    n = 8;

  if(Nblocks >= MAXBLOCKS)
    {
      printf("Task=%d: No blocks left in mymalloc().\n", ThisTask);
      endrun(813);
    }

#ifdef PEDANTIC_MEMORY_CEILING
  if(n > FreeBytes)
    {
      printf("Task=%d: Not enough memory in mymalloc(n=%g MB).  FreeBytes=%g MB\n",
	     ThisTask, n / (1024.0 * 1024.0), FreeBytes / (1024.0 * 1024.0));
      endrun(812);
    }
  Table[Nblocks] = Base + (TotBytes - FreeBytes);
  FreeBytes -= n;
#else
  Table[Nblocks] = malloc(n);
  if(!(Table[Nblocks]))
    {
      printf("failed to allocate %g MB of memory. (presently allocated=%g MB)\n",
	     n / (1024.0 * 1024.0), AllocatedBytes / (1024.0 * 1024.0));
      endrun(18);
    }
#endif

  AllocatedBytes += n;
  BlockSize[Nblocks] = n;

  Nblocks += 1;

  /*
     if(AllocatedBytes / (1024.0 * 1024.0) > Highmark)
     {
     Highmark = AllocatedBytes / (1024.0 * 1024.0);
     printf("Task=%d:   new highmark=%g MB\n",
     ThisTask, Highmark);
     fflush(stdout);
     }
   */

  return Table[Nblocks - 1];
}

void *mymalloc_msg(size_t n, char *message)
{
  if((n % 8) > 0)
    n = (n / 8 + 1) * 8;

  if(n < 8)
    n = 8;

  if(Nblocks >= MAXBLOCKS)
    {
      printf("Task=%d: No blocks left in mymalloc().\n", ThisTask);
      endrun(813);
    }

#ifdef PEDANTIC_MEMORY_CEILING
  if(n > FreeBytes)
    {
      printf
	("Task=%d: Not enough memory in for allocating (n=%g MB) for block='%s'.  FreeBytes=%g MB  AllocatedBytes=%g MB.\n",
	 ThisTask, n / (1024.0 * 1024.0), message, FreeBytes / (1024.0 * 1024.0),
	 AllocatedBytes / (1024.0 * 1024.0));
      endrun(812);
    }
  Table[Nblocks] = Base + (TotBytes - FreeBytes);
  FreeBytes -= n;
#else
  Table[Nblocks] = malloc(n);
  if(!(Table[Nblocks]))
    {
      printf("failed to allocate %g MB of memory for block='%s'. (presently allocated=%g MB)\n",
	     n / (1024.0 * 1024.0), message, AllocatedBytes / (1024.0 * 1024.0));
      endrun(18);
    }
#endif

  AllocatedBytes += n;
  BlockSize[Nblocks] = n;

  Nblocks += 1;

  return Table[Nblocks - 1];
}

void myfree(void *p)
{
  if(Nblocks == 0)
    endrun(76878);

  if(p != Table[Nblocks - 1])
    {
      printf("Task=%d: Wrong call of myfree() - not the last allocated block!\n", ThisTask);
      fflush(stdout);
      endrun(814);
    }

  Nblocks -= 1;
  AllocatedBytes -= BlockSize[Nblocks];
#ifdef PEDANTIC_MEMORY_CEILING
  FreeBytes += BlockSize[Nblocks];
#else
  free(p);
#endif
}

void myfree_msg(void *p, char *msg)
{
  if(Nblocks == 0)
    endrun(76878);

  if(p != Table[Nblocks - 1])
    {
      printf("Task=%d: Wrong call of myfree() - '%s' not the last allocated block!\n", ThisTask, msg);
      fflush(stdout);
      endrun(8141);
    }

  Nblocks -= 1;
  AllocatedBytes -= BlockSize[Nblocks];
#ifdef PEDANTIC_MEMORY_CEILING
  FreeBytes += BlockSize[Nblocks];
#else
  free(p);
#endif
}

void *myrealloc(void *p, size_t n)
{
  if((n % 8) > 0)
    n = (n / 8 + 1) * 8;

  if(n < 8)
    n = 8;

  if(Nblocks == 0)
    endrun(76879);

  if(p != Table[Nblocks - 1])
    {
      printf("Task=%d: Wrong call of myrealloc() - not the last allocated block!\n", ThisTask);
      fflush(stdout);
      endrun(815);
    }

  AllocatedBytes -= BlockSize[Nblocks - 1];
#ifdef PEDANTIC_MEMORY_CEILING
  FreeBytes += BlockSize[Nblocks - 1];
#endif

#ifdef PEDANTIC_MEMORY_CEILING
  if(n > FreeBytes)
    {
      printf("Task=%d: Not enough memory in myremalloc(n=%g MB). previous=%g FreeBytes=%g MB\n",
	     ThisTask, n / (1024.0 * 1024.0), BlockSize[Nblocks - 1] / (1024.0 * 1024.0),
	     FreeBytes / (1024.0 * 1024.0));
      endrun(812);
    }
  Table[Nblocks - 1] = Base + (TotBytes - FreeBytes);
  FreeBytes -= n;
#else
  Table[Nblocks - 1] = realloc(Table[Nblocks - 1], n);
  if(!(Table[Nblocks - 1]))
    {
      printf("failed to reallocate %g MB of memory. previous=%g FreeBytes=%g MB\n",
	     n / (1024.0 * 1024.0), BlockSize[Nblocks - 1] / (1024.0 * 1024.0),
	     FreeBytes / (1024.0 * 1024.0));
      endrun(18123);
    }
#endif

  AllocatedBytes += n;
  BlockSize[Nblocks - 1] = n;

  return Table[Nblocks - 1];
}

#else

void mymalloc_init(void)
{
  AllocatedBytes = 0;
}

void *mymalloc(size_t n)
{
  void *ptr;

  if(n < 8)
    n = 8;

  ptr = malloc(n);

  if(!(ptr))
    {
      printf("failed to allocate %g MB of memory.\n", n / (1024.0 * 1024.0));
      endrun(14);
    }

  return ptr;
}

void *mymalloc_msg(size_t n, char *message)
{
  void *ptr;

  if(n < 8)
    n = 8;

  ptr = malloc(n);

  if(!(ptr))
    {
      printf("failed to allocate %g MB of memory when trying to allocate '%s'.\n",
	     n / (1024.0 * 1024.0), message);
      endrun(14);
    }

  return ptr;
}

void *myrealloc(void *p, size_t n)
{
  void *ptr;

  ptr = realloc(p, n);

  if(!(ptr))
    {
      printf("failed to re-allocate %g MB of memory.\n", n / (1024.0 * 1024.0));
      endrun(15);
    }

  return ptr;
}

void myfree(void *p)
{
  free(p);
}

void myfree_msg(void *p, char *msg)
{
  free(p);
}

#endif


#pragma once

#include <functional>
#include <gsl/gsl>
#include <memory>
#include <vector>
#include "halley/text/halleystring.h"
#include "halley/maths/vector2.h"
#include "halley/maths/vector3.h"
#include <memory>

namespace Halley
{
	class AudioPosition;
	class AudioClip;
	class AudioEvent;
	class IAudioClip;
	class AudioEmitterBehaviour;

    namespace AudioConfig {
        constexpr int sampleRate = 48000;
		constexpr int maxChannels = 8;
        using SampleFormat = float;
    }

	class AudioDevice
	{
	public:
		virtual ~AudioDevice() {}
		virtual String getName() const = 0;
	};

	struct alignas(64) AudioSamplePack
	{
		constexpr static int NumSamples = 16;
		std::array<AudioConfig::SampleFormat, NumSamples> samples; // AVX-512 friendly
	};

	enum class AudioSampleFormat
	{
		Undefined,
		Int16,
		Int32,
		Float
	};

	template <>
	struct EnumNames<AudioSampleFormat> {
		constexpr std::array<const char*, 4> operator()() const {
			return{{
				"undefined",
				"int16",
				"int32",
				"float"
			}};
		}
	};

	class AudioSpec
	{
	public:
		int sampleRate;
		int numChannels;
		int bufferSize;
		AudioSampleFormat format;

		AudioSpec() {}
		AudioSpec(int sampleRate, int numChannels, int bufferSize, AudioSampleFormat format)
			: sampleRate(sampleRate)
			, numChannels(numChannels)
			, bufferSize(bufferSize)
			, format(format)
		{}
	};

	class AudioListenerData
	{
	public:
		Vector3f position;

		AudioListenerData() {}
		AudioListenerData(Vector3f position)
			: position(position)
		{}
	};

	using AudioCallback = std::function<void()>;

	class AudioOutputAPI
	{
	public:
		virtual ~AudioOutputAPI() {}

		virtual Vector<std::unique_ptr<const AudioDevice>> getAudioDevices() = 0;
		virtual AudioSpec openAudioDevice(const AudioSpec& requestedFormat, const AudioDevice* device = nullptr, AudioCallback prepareAudioCallback = AudioCallback()) = 0;
		virtual void closeAudioDevice() = 0;

		virtual void startPlayback() = 0;
		virtual void stopPlayback() = 0;

		virtual void queueAudio(gsl::span<const float> data) = 0;
		virtual bool needsMoreAudio() = 0;

		virtual bool needsAudioThread() const = 0;
	};

	class IAudioHandle
	{
	public:
		virtual ~IAudioHandle() {}

		virtual void setGain(float gain) = 0;
		virtual void setVolume(float volume) = 0;
		virtual void setPosition(Vector2f pos) = 0;
		virtual void setPan(float pan) = 0;

		virtual void stop(float fadeTime = 0.0f) = 0;
		virtual bool isPlaying() const = 0;
		virtual void setBehaviour(std::unique_ptr<AudioEmitterBehaviour> behaviour) = 0;
	};
	using AudioHandle = std::shared_ptr<IAudioHandle>;

	class AudioAPI
	{
	public:
		virtual ~AudioAPI() {}

		virtual Vector<std::unique_ptr<const AudioDevice>> getAudioDevices() = 0;
		virtual void startPlayback(int deviceNumber = 0) = 0;
		virtual void stopPlayback() = 0;
		virtual void pausePlayback() = 0;
		virtual void resumePlayback() = 0;

		virtual AudioHandle postEvent(const String& name, AudioPosition position) = 0;

		virtual AudioHandle play(std::shared_ptr<const IAudioClip> clip, AudioPosition position, float volume = 1.0f, bool loop = false) = 0;
		virtual AudioHandle playMusic(const String& eventName, int track = 0) = 0;
		virtual AudioHandle getMusic(int track = 0) = 0;
		virtual void stopMusic(int track = 0, float fadeOutTime = 0.0f) = 0;
		virtual void stopAllMusic(float fadeOutTime = 0.0f) = 0;

		virtual void setMasterVolume(float gain = 1.0f) = 0;
		virtual void setGroupVolume(const String& groupName, float gain = 1.0f) = 0;

		virtual void setListener(AudioListenerData listener) = 0;
	};
}


#pragma once
#include <gsl/span>
#include "halley/core/api/audio_api.h"
#include "audio_buffer.h"

namespace Halley
{
	class AudioMixer
	{
	public:
		static void mixAudio(AudioSamplesConst src, AudioSamples dst, float gainStart, float gainEnd);
		static void mixAudio(AudioMultiChannelSamplesConst src, AudioMultiChannelSamples dst, float gainStart, float gainEnd);
		static void mixAudio(AudioMultiChannelSamples src, AudioMultiChannelSamples dst, float gainStart, float gainEnd);

		static void interleaveChannels(AudioSamples dst, gsl::span<AudioBuffer*> srcs);
		static void concatenateChannels(AudioSamples dst, gsl::span<AudioBuffer*> srcs);
		static void compressRange(AudioSamples buffer);

		static void zero(AudioSamples dst);
		static void zero(AudioMultiChannelSamples dst, size_t nChannels = 8);
		static void zeroRange(AudioMultiChannelSamples dst, size_t nChannels, size_t start, size_t len = std::numeric_limits<size_t>::max());
		static void copy(AudioMultiChannelSamples dst, AudioMultiChannelSamples src, size_t nChannels = 8);
		static void copy(AudioSamples dst, AudioSamples src);
		static void copy(AudioSamples dst, AudioSamples src, float gainStart, float gainEnd);
	};
}


/*
 *  read_parameters.h
 *  Spike
 *	parse simple name/value pairs
 *
 *  Created by Ben Evans on 28/11/2008.
 *  Copyright 2008 University of Oxford. All rights reserved.
 *
 */

#ifndef _READ_PARAMETERS_H
#define _READ_PARAMETERS_H

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

#include "globals.h"
#include "parameters.h"
#include "utils.h"
#include <gsl/gsl_sf_gamma.h> // N choose M funcion

#define MAXLEN 256
#define BUFFER 512 // Make larger for comments or break up comments
#define VECBUFF 4

char * trim(char * string);
int parse_string(PARAMS * params, char * string);
int read_parameters(PARAMS * params, char * paramfile);
int parseIntVector(char * string, int ** array);
int parseFloatVector(char * string, float ** array);
int printParameters(PARAMS * mp, char * paramfile);
void printIntArray(FILE * fp, char * name, int * array, int len);
void printFloatArray(FILE * fp, char * name, float * array, int len);

#endif


#include <petsc.h>
#include <bpetsc_impl.h>
#include <assert.h>

#define LEFT_SUBSPACE Full
  #define RIGHT_SUBSPACE Full
    #include <buildmat_template.h>
  #undef RIGHT_SUBSPACE
#undef LEFT_SUBSPACE

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

  PetscErrorCode ierr;
  PetscScalar* x_array;
  PetscScalar* b_array;
  shell_context ctx;
  data_Full subspace_data;

  /* test identity */
  ctx.nmasks = 1;
  ierr = PetscMalloc1(1, &(ctx.masks));CHKERRQ(ierr);
  ierr = PetscMalloc1(2, &(ctx.mask_offsets));CHKERRQ(ierr);
  ierr = PetscMalloc1(1, &(ctx.signs));CHKERRQ(ierr);
  ierr = PetscMalloc1(1, &(ctx.real_coeffs));CHKERRQ(ierr);

  ctx.masks[0] = 0;
  ctx.mask_offsets[0] = 0;
  ctx.mask_offsets[1] = 1;
  ctx.signs[0] = 0;
  ctx.real_coeffs[0] = 1.;

  subspace_data.L = -1;
  ctx.left_subspace_data = &subspace_data;
  ctx.right_subspace_data = &subspace_data;

  #define DIM 100

  ierr = PetscMalloc1(DIM, &x_array);CHKERRQ(ierr);
  ierr = PetscMalloc1(DIM, &b_array);CHKERRQ(ierr);

  for (PetscInt i = 0; i < DIM; ++i) {
    x_array[i] = i;
    b_array[i] = 0;
  }

  MatMult_CPU_kernel_Full_Full(x_array, b_array, &ctx, 0, DIM, 0, DIM);

  for (PetscInt i = 0; i < DIM; ++i) {
    assert(b_array[i] == i);
  }

  ierr = PetscFree(x_array);CHKERRQ(ierr);
  ierr = PetscFree(b_array);CHKERRQ(ierr);

  #undef DIM

  ierr = PetscFree(ctx.masks);CHKERRQ(ierr);
  ierr = PetscFree(ctx.mask_offsets);CHKERRQ(ierr);
  ierr = PetscFree(ctx.signs);CHKERRQ(ierr);
  ierr = PetscFree(ctx.real_coeffs);CHKERRQ(ierr);

  return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include <getopt.h>
#include <mpi.h>
#include "compearth.h"
#include "parmt_utils.h"
#ifdef PARMT_USE_INTEL
#include <mkl_cblas.h>
#else
#include <cblas.h>
#endif
#include "parmt_polarity.h"
#include "parmt_postProcess.h"
#include "parmt_mtsearch.h"
#include "iscl/array/array.h"
#include "iscl/memory/memory.h"
#include "iscl/os/os.h"

#define PROGRAM_NAME "postmt"
#define OUTDIR "postprocess"
#define WAVOUT_DIR "obsest"

static int parseArguments(int argc, char *argv[], char iniFile[PATH_MAX]);
static void printUsage(void);
int parmt_freeData(struct parmtData_struct *data);

int main(int argc, char *argv[])
{
    struct parmtGeneralParms_struct parms;
    struct parmtData_struct data;
    struct parmtPolarityParms_struct polarityParms;
    struct polarityData_struct polarityData;
    FILE *ofl;
    char fname[PATH_MAX];
    char iniFile[PATH_MAX];
    char programNameIn[256];
    bool lpol;
    double U[9], Muse[6], Mned[6], lam[3], *betas, *deps, *depMPDF, *depMagMPDF,
           *G, *gammas, *kappas,
           *sigmas, *thetas, *M0s, *phi, *var, dip, epoch,
           lagTime, Mw, phiLoc, xnorm, xsum;
    int ierr, iobs, imtopt, jb, jg, jk, jloc, jm, joptLoc,
        js, jt, k, lag, myid, nb, ng, nk, nlags, nlocs,
        nm, nmt, npmax, npts, nprocs, ns, nt,
        provided;
    bool ldefault;
    // Start MPI
    MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
    // Initialize
    depMPDF = NULL;
    depMagMPDF = NULL;
    betas = NULL;
    deps = NULL;
    gammas = NULL;
    kappas = NULL;
    sigmas = NULL;
    thetas = NULL;
    M0s = NULL;
    phi = NULL;
    memset(&parms, 0, sizeof(struct parmtGeneralParms_struct));
    memset(&data, 0, sizeof(struct parmtData_struct));
    memset(&polarityParms, 0, sizeof(struct parmtPolarityParms_struct));
    memset(&polarityData, 0, sizeof(struct polarityData_struct));
    // Parse the input arguments
    ierr = parseArguments(argc, argv, iniFile);
    if (ierr != 0)
    {
        if (ierr ==-2){return 0;}
        printf("%s: Error parsing arguments\n", PROGRAM_NAME);
        return EXIT_FAILURE;
    }
    // Load the ini file - from this we should be able to deduce the archive
    ierr = parmt_utils_readGeneralParms(iniFile, &parms);
    if (ierr != 0)
    {
        printf("%s: Error reading general parameters\n", PROGRAM_NAME);
        return EXIT_FAILURE;
    }
    if (!os_path_isfile(parms.postmtFile))
    {
        printf("%s: Archive file %s doesn't exist\n",
               PROGRAM_NAME, parms.postmtFile);
        return EXIT_FAILURE;
    }
    ierr += parmt_utils_readPolarityParms(iniFile, &polarityParms);

    // TODO make this a config
    if (!os_path_isdir(OUTDIR))
    {
        os_makedirs(OUTDIR);
    }
    if (!os_path_isdir(WAVOUT_DIR))
    {
        os_makedirs(WAVOUT_DIR);
    }
    // Load the data
    printf("%s: Reading data...\n", PROGRAM_NAME);
    ierr = utils_dataArchive_readAllWaveforms(parms.dataFile, &data);
    if (ierr != 0)
    {
        printf("%s: Error reading data\n", PROGRAM_NAME);
        goto ERROR;
    }
    data.est = (struct sacData_struct *)
               calloc((size_t) data.nobs, sizeof(struct sacData_struct));
    // Read the archive
    printf("%s: Reading archive %s...\n", PROGRAM_NAME, parms.postmtFile);
printf("%s %s %s\n", parms.resultsDir, parms.projnm, parms.resultsFileSuffix);
    ierr = parmt_io_readObjfnArchive64f(
                   //parms.resultsDir, parms.projnm, parms.resultsFileSuffix,
                   parms.postmtFile, //parms.parmtArchive,
                   programNameIn,
                   &nlocs, &deps,
                   &nm, &M0s,
                   &nb, &betas,
                   &ng, &gammas,
                   &nk, &kappas,
                   &ns, &sigmas,
                   &nt, &thetas,
                   &nmt, &phi);
    if (ierr != 0)
    {
        printf("%s: Error loading archive\n", PROGRAM_NAME);
        goto ERROR;
    }
/*
double *phi1;
    ierr = parmt_io_readObjfnArchive64f(
                   "bw", parms.projnm, "bodyWaves",
                   &nlocs, &deps,
                   &nm, &M0s,
                   &nb, &betas,
                   &ng, &gammas,
                   &nk, &kappas,
                   &ns, &sigmas,
                   &nt, &thetas,
                   &nmt, &phi1);
for (int imt=0; imt<nmt; imt++)
{
  phi[imt] = (12.0*phi1[imt] + 11*phi[imt]);
}
        ierr = parmt_io_createObjfnArchive64f("joint", parms.projnm,
                                              "joint",
                                              25, //data.nobs,
                                              nlocs, deps,
                                              nm, M0s,
                                              nb, betas,
                                              ng, gammas,
                                              nk, kappas,
                                              ns, sigmas,
                                              nt, thetas);
        ierr = parmt_io_writeObjectiveFunction64f(
                   "joint", parms.projnm, "joint",
                   nmt, phi);
return 0;
*/
    // Get the optimum moment tensor for the waveforms
    ierr = marginal_getOptimum(nlocs, nm, nb,
                               ng, nk, ns, nt,
                               phi,
                               &jloc, &jm, &jb, &jg,
                               &jk, &js, &jt);
    if (ierr != 0)
    {
        printf("%s: Failed to get optimum\n", PROGRAM_NAME);
        goto ERROR;
    }
    imtopt = jloc*nm*nb*ng*nk*ns*nt
           +      jm*nb*ng*nk*ns*nt
           +         jb*ng*nk*ns*nt
           +            jg*nk*ns*nt
           +               jk*ns*nt
           +                  js*nt
           +                     jt;
//printf("%d %e\n", jm, M0s[jm]);
//getchar();
    compearth_m02mw(1, CE_KANAMORI_1978, &M0s[jm], &Mw);
    joptLoc = jloc;
//printf("%d %d %d %d %d %d %d\n", jloc, jm, jg, jb, jk, js, jt);
//jloc =4; jm = 5; jg =1; jb=0; jk=11; js=2; jt=4;

    printf("%s: Optimum information:\n", PROGRAM_NAME);
    printf("        Value: %f\n", phi[imtopt]);
    printf("        Depth: %f (km)\n", deps[jloc]);
    printf("        Magnitude: %f\n", Mw);
    printf("        Lune longitude: %f (deg)\n", gammas[jg]*180.0/M_PI);
    printf("        Lune latitude: %f (deg)\n", (M_PI_2 - betas[jb])*180.0/M_PI);
    printf("        Strike: %f (deg)\n", kappas[jk]*180.0/M_PI);
    printf("        Slip: %f (deg)\n", sigmas[js]*180.0/M_PI);
    printf("        Dip: %f (deg)\n", thetas[jt]*180.0/M_PI);
    // Display the moment tensor in USE format which is useful for obspy and gmt
        double gammaOpt = gammas[jg]*180.0/M_PI;
        double deltaOpt = 90.0-betas[jb]*180.0/M_PI;
        double kappaOpt = kappas[jk]*180.0/M_PI;
        double thetaOpt = thetas[jt]*180.0/M_PI;
        double sigmaOpt = sigmas[js]*180.0/M_PI;
        compearth_TT2CMT(1, &gammaOpt,
                         &deltaOpt,
                         &M0s[jm],
                         &kappaOpt,
                         &thetaOpt,
                         &sigmaOpt,
                         Muse, lam, U);

/*
    compearth_tt2cmt(gammas[jg]*180.0/M_PI,
                     (M_PI/2.0 - betas[jb])*180.0/M_PI,
                     M0s[jm],
                     kappas[jk]*180.0/M_PI,
                     thetas[jt]*180.0/M_PI,
                     sigmas[js]*180.0/M_PI,
                     Muse, lam, U);
*/
/*
Muse[0] = 28200000000000000.;  // mrr
Muse[1] = 283100000000000000.; // mtt
Muse[2] =-311300000000000000.; // mpp
Muse[3] =-243300000000000000.; // mrt
Muse[4] =-149900000000000000.; // mrp
Muse[5] = 467500000000000000.; // mtp
compearth_convertMT(1, CE_USE,  CE_NED, Muse, Mned);
*/
    ierr = parmt_discretizeMT64f(1, &gammas[jg],
                                 1, &betas[jb],
                                 1, &M0s[jm],
                                 1, &kappas[jk],
                                 1, &thetas[jt],
                                 1, &sigmas[js],
                                 6, 1, Mned);

    printf("mtUSE =[%.6e,%.6e,%.6e,%.6e,%.6e,%.6e]\n",
           Muse[0], Muse[1], Muse[2], Muse[3], Muse[4], Muse[5]);
    printf("mtNED =[%.6e,%.6e,%.6e,%.6e,%.6e,%.6e]\n",
           Mned[0], Mned[1], Mned[2], Mned[3], Mned[4], Mned[5]);
//printf("%e\n", M0s[jm]);
//compearth_CMT2m0(1, 1, Mned, &M0s[jm]);
//printf("%e\n", M0s[jm]);
//getchar();
    // Compute the polarities
    lpol = true;
    ierr = parmt_polarity_computeTTimesGreens(MPI_COMM_WORLD, polarityParms,
                                              data, &polarityData);
    if (ierr != 0)
    {
        printf("%s: Error computing polarity Green's functions\n",
               PROGRAM_NAME);
        ierr = 1;
        lpol = false;
    }
    if (lpol && polarityData.nPolarity > 0)
    {
        double *Gpol = memory_calloc64f(6*polarityData.nPolarity);
        double *est = memory_calloc64f(polarityData.nPolarity);
        double phiPol;
        int ipol, kt;
        kt = jloc*polarityData.nPolarity;
printf("G\n");
        for (ipol=0; ipol<polarityData.nPolarity; ipol++)
        {
            Gpol[6*ipol+0] = polarityData.Gxx[kt+ipol];
            Gpol[6*ipol+1] = polarityData.Gyy[kt+ipol];
            Gpol[6*ipol+2] = polarityData.Gzz[kt+ipol];
            Gpol[6*ipol+3] = polarityData.Gxy[kt+ipol];
            Gpol[6*ipol+4] = polarityData.Gxz[kt+ipol];
            Gpol[6*ipol+5] = polarityData.Gyz[kt+ipol];
printf("%f %f %f %f %f %f\n", Gpol[6*ipol+0], Gpol[6*ipol+1], Gpol[6*ipol+2],
                     Gpol[6*ipol+3], Gpol[6*ipol+4], Gpol[6*ipol+5]);
        }
        cblas_dgemv(CblasRowMajor, CblasNoTrans,
                    polarityData.nPolarity, 6, 1.0, Gpol, 6,
                    Mned, 1, 0.0, est, 1);
        for (ipol=0; ipol<polarityData.nPolarity; ipol++)
        {
            est[ipol] = copysign(1.0, est[ipol]);
            printf("Polarity: obs %f; est %f\n",
                   polarityData.polarity[ipol], est[ipol]);
        }

    }
    // Write the output file

    struct globalMapOpts_struct globalMap;
    memset(&globalMap, 0, sizeof(struct globalMapOpts_struct));
    strcpy(globalMap.outputScript, "gmtScripts/globalMap.sh");
    strcpy(globalMap.psFile, "globalMap.ps");
    array_copy64f_work(6, Muse, globalMap.mts);
    globalMap.basis = CE_USE;
    globalMap.evla = data.data[0].header.evla;
    globalMap.evlo = data.data[0].header.evlo;
    globalMap.evdp = deps[jloc];
    globalMap.lwantMT = true;
    globalMap.lwantPolarity = true;
printf("%f %f\n", globalMap.mts[0], globalMap.mts[1]);
    ierr = postmt_gmtHelper_writeGlobalMap(globalMap, data.nobs, data.data);
/*
printf("overriding ned and joptloc\n");
joptLoc=5;
Mned[0] = 8.526325e+15;
Mned[1] =-8.707938e+16;
Mned[2] = 2.226786e+17;
Mned[3] =-5.768950e+17;
Mned[4] =-3.469420e+17;
Mned[5] =-2.756277e+17;
*/
    // Compute the scaling factor
    xnorm = marginal_computeNormalization(nlocs, deps,
                                          nm, M0s,
                                          nb, betas,
                                          ng, gammas,
                                          nk, kappas,
                                          ns, sigmas,
                                          nt, thetas,
                                          phi, &ierr);
    if (xnorm <= 0.0 || ierr != 0)
    {
        printf("%s: Failed to compute normalization factor - setting to 1\n",
               PROGRAM_NAME);
        ierr = 0;
        xnorm = 1.0;
    }
    printf("%s: Scaling factor: %e\n", PROGRAM_NAME, xnorm);
    cblas_dscal(nmt, 1.0/xnorm, phi, 1);
    printf("%s: Computing pure histograms\n", PROGRAM_NAME);

    double *locHist, *magHist, *betaHist, *gammaHist, *kappaHist, *sigmaHist, *thetaHist;
    ierr = postmt_gmtHelper_makeRegularHistograms(nlocs, nm,
                                                  nb, ng, nk, ns, nt,
                                                  nmt, phi,
                                                  &locHist, &magHist, &betaHist,
                                                  &gammaHist, &kappaHist, &sigmaHist,
                                                  &thetaHist);
    int i;
/*
    printf("Depths\n");
    for (i=0; i<nlocs; i++)
    {
        printf("%f %f\n", deps[i], locHist[i]);
    }
    printf("Magnitudes\n");
    for (i=0; i<nm; i++)
    {
        compearth_m02mw(1, CE_KANAMORI_1978, &M0s[i], &Mw);
        printf("%f %f\n", Mw, magHist[i]);
    }
    printf("Gammas\n");
    for (i=0; i<ng; i++)
    {
        printf("%f %f\n", gammas[i]*180.0/M_PI, gammaHist[i]);
    }
*/
    printf("Betas\n");
    for (i=0; i<nb; i++)
    {
        printf("%f %f\n", 90.0 - betas[i]*180.0/M_PI, betaHist[i]);
    }
/*
    printf("Kappas\n");
    for (i=0; i<nk; i++)
    {
        printf("%f %f\n", kappas[i]*180.0/M_PI, kappaHist[i]);
    }
    printf("Sigmas\n");
    for (i=0; i<ns; i++)
    {
        printf("%f %f\n", sigmas[i]*180.0/M_PI, sigmaHist[i]);
    }
    printf("Thetas\n");
    for (i=0; i<nt; i++)
    {
        printf("%f %f\n", thetas[i]*180.0/M_PI, thetaHist[i]);
    }
*/
const char *scriptFile = "gmtScripts/boxes.sh";
const char *psFile = "boxes.ps";
ierr = postmt_gmtHelper_writeBetaBoxes(false, false, scriptFile, psFile,
                                       nb, jb, betas, betaHist);
ierr = postmt_gmtHelper_writeGammaBoxes(true, false, scriptFile, psFile,
                                        ng, jg, gammas, gammaHist);
ierr = postmt_gmtHelper_writeKappaBoxes(true, false, scriptFile, psFile,
                                        nk, jk, kappas, kappaHist);
ierr = postmt_gmtHelper_writeSigmaBoxes(true, false, scriptFile, psFile,
                                        ns, js, sigmas, sigmaHist);
ierr = postmt_gmtHelper_writeThetaBoxes(true, false, scriptFile, psFile,
                                        nt, jt, thetas, thetaHist);
ierr = postmt_gmtHelper_writeMagnitudeBoxes(true, false, scriptFile, psFile,
                                            nm, jm, M0s, magHist);
ierr = postmt_gmtHelper_writeDepthBoxes(true, true, scriptFile, psFile,
                                        nlocs, joptLoc, deps, locHist);

    // Start the post-processing - compute the depgh magnitude mPDFs
    printf("%s: Computing the depth MPDF's\n", PROGRAM_NAME);
    depMagMPDF = memory_calloc64f(nm*nlocs);
    depMPDF = memory_calloc64f(nlocs);
    ierr = marginal_computeDepthMPDF(nlocs,
                                     nm, M0s,
                                     nb, betas,
                                     ng, gammas,
                                     nk, kappas,
                                     ns, sigmas,
                                     nt, thetas,
                                     phi, depMagMPDF, depMPDF);
    if (ierr != 0)
    {
        printf("%s: Error computing depthMPDF\n", PROGRAM_NAME);
        goto ERROR;
    }
    // Print the station list for my edification
    for (k=0; k<data.nobs; k++)
    {
        printf("%8.3f %8.3f %6s\n", data.data[k].header.stlo,
                                    data.data[k].header.stla,
                                    data.data[k].header.kstnm);
    }
int im, iloc;
    for (im=0; im<nm; im++)
    {
        compearth_m02mw(1, CE_KANAMORI_1978, &M0s[im], &Mw);
        printf("Writing magnitude: %f\n", Mw);
        memset(fname, 0, PATH_MAX*sizeof(char));
        sprintf(fname, "%s/%s_dep.%d.txt", OUTDIR, parms.projnm, im+1);
        ofl = fopen(fname, "w");
        for (iloc=0; iloc<nlocs; iloc++)
        {
            jloc = im*nlocs + iloc;
            fprintf(ofl, "%e %e\n", deps[iloc], depMagMPDF[jloc]);
        }
        fclose(ofl);
    }
    memset(fname, 0, PATH_MAX*sizeof(char));
    sprintf(fname, "%s/%s_dep.txt", OUTDIR, parms.projnm);
    ofl = fopen(fname, "w");
    for (iloc=0; iloc<nlocs; iloc++)
    {
        fprintf(ofl, "%e %e\n", deps[iloc], depMPDF[iloc]);
    }
    fclose(ofl);
    // Compute the optimal synthetics
    xsum = 0.0;
    for (iobs=0; iobs<data.nobs; iobs++)
    {
        // Extract the depth waveform
        npts = data.data[iobs].npts;
        npmax = npts;
        G = memory_calloc64f(6*npmax);
        var = memory_calloc64f(npmax);
        ierr = parmt_utils_setDataOnG(iobs, joptLoc, npmax, data, G);
        if (ierr != 0)
        {
            printf("%s: Failed to set data on G\n", PROGRAM_NAME);
            goto ERROR;
        }
        // Compute the lag time
        nlags = 0;
        lag = 0;
        if (parms.lwantLags)
        {
            //lagTime = data.data[iobs].header.user0;
            //if (lagTime < 0.0){lagTime = parms.defaultMaxLagTime;}
            lagTime = parmt_utils_getLagTime(data.data[iobs],
                                             parms.defaultMaxLagTime,
                                             &ldefault);
            nlags = (int) (lagTime/data.data[iobs].header.delta + 0.5);
        }
        ierr = parmt_mtSearchL164f(6, 1,
                                   npts, 1,
                                   nlags, parms.lwantLags,
                                   parms.lrescale,
                                   &G[0*npmax], &G[1*npmax], &G[2*npmax],
                                   &G[3*npmax], &G[4*npmax], &G[5*npmax],
                                   NULL, Mned, data.data[iobs].data,
                                   &phiLoc, var, &lag);
        if (ierr != 0)
        {
            printf("%s: Error computing lags for waveform %d\n",
                   PROGRAM_NAME, iobs);
            goto ERROR;
        }
        // Compute the synthetic
        k = iobs*data.nlocs + joptLoc;
        parmt_utils_sacGrnsToEst(data.data[iobs],
                                 data.sacGxx[k], data.sacGyy[k],
                                 data.sacGzz[k], data.sacGxy[k],
                                 data.sacGxz[k], data.sacGyz[k],
                                 Mned,
                                 parms.lrescale,
                                 &data.est[iobs]);
        // Fix the timing
        ierr = sacio_getEpochalStartTime(data.est[iobs].header, &epoch);
        epoch = epoch + (double) lag*data.data[iobs].header.delta;
        sacio_setEpochalStartTime(epoch, &data.est[iobs].header);
        // Write the file
        memset(fname, 0, PATH_MAX*sizeof(char));
        sprintf(fname, "%s/%s.%s.%s.%s.SAC",
                WAVOUT_DIR,
                data.data[iobs].header.knetwk,
                data.data[iobs].header.kstnm,
                data.data[iobs].header.kcmpnm,
                data.data[iobs].header.khole);
        sacio_writeTimeSeriesFile(fname, data.data[iobs]);
        memset(fname, 0, PATH_MAX*sizeof(char));
        sprintf(fname, "%s/%s.%s.%s.%s.EST.SAC",
                WAVOUT_DIR,
                data.data[iobs].header.knetwk,
                data.data[iobs].header.kstnm,
                data.data[iobs].header.kcmpnm,
                data.data[iobs].header.khole);
        sacio_writeTimeSeriesFile(fname, data.est[iobs]);
        printf("Waveform %6s has lag %+4d=%+8.3e (s) and a fit of %f\n",
               data.data[iobs].header.kstnm,
               lag, lag*data.data[iobs].header.delta, phiLoc);
        memory_free64f(&G);
        memory_free64f(&var);
        xsum = xsum + phiLoc;
    }
    printf("Average %f\n", xsum/(double) data.nobs );
/*
double *db = memory_calloc64f(nb);
double *dg = memory_calloc64f(ng);
double *dt = memory_calloc64f(nt);
postprocess_computeBetaCellSpacing(nb, betas, db);
 printf("\n");
postprocess_computeGammaCellSpacing(ng, gammas, dg);
printf("\n");
postprocess_computeThetaCellSpacing(nt, thetas, dt);
*/
    // Free memory
ERROR:;
    parmt_freeData(&data);
    memory_free64f(&depMagMPDF);
    memory_free64f(&depMPDF);
    memory_free64f(&deps);
    memory_free64f(&M0s);
    memory_free64f(&betas);
    memory_free64f(&gammas);
    memory_free64f(&kappas);
    memory_free64f(&sigmas);
    memory_free64f(&thetas);
    memory_free64f(&phi);
    iscl_finalize();
    MPI_Finalize();
    return ierr;
}
//============================================================================//
/*!
 * @brief Parses the input arguments to get the ini file anme
 */
static int parseArguments(int argc, char *argv[], char iniFile[PATH_MAX])
{
    bool linFile;
    memset(iniFile, 0, PATH_MAX*sizeof(char));
    while (true)
    {
        static struct option longOptions[] =
        {
            {"help", no_argument, 0, '?'},
            {"help", no_argument, 0, 'h'},
            {"ini_file", required_argument, 0, 'i'},
            {0, 0, 0, 0}
        };
        int c, optionIndex;
        c = getopt_long(argc, argv, "?hi:",
                        longOptions, &optionIndex);
        if (c ==-1){break;}
        if (c == 'i')
        {
            strcpy(iniFile, (const char *) optarg);
            linFile = true;
        }
        else if (c == 'h' || c == '?')
        {
            printUsage();
            return -2;
        }
        else
        {
            printf("%s: Unknown options: %s\n",
                   PROGRAM_NAME, argv[optionIndex]);
        }
    }
    if (!linFile)
    {
        printf("%s: Error must specify ini file\n\n", PROGRAM_NAME);
        printUsage();
        return -1;
    }
    return 0;
}

static void printUsage(void)
{
    printf("Usage:\n   postmt -i input_file\n\n");
    printf("Required arguments:\n");
    printf("   -i input_file specifies the initialization file\n");
    printf("\n");
    printf("Optional arguments:\n");
    printf("   -h displays this message\n");
    return;
}

int parmt_freeData(struct parmtData_struct *data)
{
    int i;
    if (data->nobs > 0 && data->nlocs > 0 &&
        data->sacGxx != NULL && data->sacGyy != NULL && data->sacGzz != NULL &&
        data->sacGxy != NULL && data->sacGxz != NULL && data->sacGyz != NULL)
    {
        for (i=0; i<data->nobs*data->nlocs; i++)
        {
            sacio_freeData(&data->sacGxx[i]);
            sacio_freeData(&data->sacGyy[i]);
            sacio_freeData(&data->sacGzz[i]);
            sacio_freeData(&data->sacGxy[i]);
            sacio_freeData(&data->sacGxz[i]);
            sacio_freeData(&data->sacGyz[i]);
        }
        free(data->sacGxx);
        free(data->sacGyy);
        free(data->sacGzz);
        free(data->sacGxy);
        free(data->sacGxz);
        free(data->sacGyz);
    }
    if (data->nobs > 0 && data->data != NULL)
    {
        for (i=0; i<data->nobs; i++)
        {
            sacio_freeData(&data->data[i]);
        }
        free(data->data);
    }
    if (data->nobs > 0 && data->est != NULL)
    {
        free(data->est);
    }
    memset(data, 0, sizeof(struct parmtData_struct));
    return 0;
}