#include <boost/config.hpp>
#include <boost/cstdfloat.hpp>
#include <boost/core/demangle.hpp>
#include <boost/charconv.hpp>
#include <system_error>
#include <string_view>
#include <type_traits>
#include <typeinfo>
#include <stdfloat>
#include <charconv>
#include <concepts>
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <string>
#include <cstdio>
#include <array>
#include <quadmath.h>



#if defined(BOOST_HAS_FLOAT128) && defined(__STDCPP_FLOAT128_T__) && !defined(BOOST_NO_CXX17_HDR_CHARCONV)
//	coming soon https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113260
namespace std
{
inline from_chars_result from_chars(const char* first, const char* last, boost::math::cstdfloat::detail::float_internal128_t& value, chars_format fmt = chars_format::general) noexcept
{
	return from_chars(first, last, reinterpret_cast<float128_t&>(value), fmt);
}
inline to_chars_result to_chars(char* first, char* last, boost::math::cstdfloat::detail::float_internal128_t value, chars_format fmt, int precision) noexcept
{
	return to_chars(first, last, float128_t(value), fmt, precision);
}
inline to_chars_result to_chars(char* first, char* last, boost::math::cstdfloat::detail::float_internal128_t value, chars_format fmt) noexcept
{
	return to_chars(first, last, float128_t(value), fmt);
}

} // std
#endif

namespace test
{
using buffer_type = std::array<char, 128>;

inline static constexpr std::string_view
	scientific{"scientific"},
	general   {"general   "},
	fixed     {"fixed     "},
	hex       {"hex       "};

template <typename Type>
inline std::string	nameof()
{
	return boost::core::demangle(typeid(Type).name());
}

inline constexpr std::string_view	to_string(const std::chars_format format)	noexcept
{
	switch (format)
	{
		case std::chars_format::scientific:	return scientific;
		case std::chars_format::fixed:		return fixed;
		case std::chars_format::hex:		return hex;
		default:							return general;
	}
}

inline constexpr boost::charconv::chars_format to_format(const std::chars_format format)	noexcept
{
	switch (format)
	{
		case std::chars_format::scientific:	return boost::charconv::chars_format::scientific;
		case std::chars_format::fixed:		return boost::charconv::chars_format::fixed;
		case std::chars_format::hex:		return boost::charconv::chars_format::hex;
		default:							return boost::charconv::chars_format::general;
	}
}

inline constexpr std::to_chars_result	to_result(const boost::charconv::to_chars_result& result)	noexcept
{
	return {result.ptr, result.ec};
}

inline void show_result(const std::string_view& info, const std::chars_format format, const buffer_type& buffer, const std::to_chars_result& result)
{
	const size_t
		size = size_t(result.ptr - buffer.data());

	std::cout << '(' << info << ", " << to_string(format) << ")\t";
	if (size <= buffer.size())
	{
		if (result.ec == std::errc{})
		{
			std::cout << std::string_view{buffer.data(), result.ptr} << '\t';
		}
		else
		{
			std::cout << "{}\t";
		}
	}
	else
	{
		std::cout << "BOF!\t";
	}
	std::cout << '(' << size << ", " << std::make_error_code(result.ec).message() << ")\n";
}

template <std::floating_point Type>
inline void show_format(buffer_type& buffer, const Type value, const std::chars_format format)
{
	std::to_chars_result
		result;

	buffer.fill(0);
	result = std::to_chars(buffer.data(), buffer.data()+buffer.size(), value, format);
	show_result("std  ", format, buffer, result);

	buffer.fill(0);
	result = to_result(boost::charconv::to_chars(buffer.data(), buffer.data()+buffer.size(), value, to_format(format)));
	show_result("boost", format, buffer, result);
	std::cout << '\n';
}

template <std::floating_point Type>
void show(const Type value)
{
	buffer_type
		buffer;

	std::cout << nameof<Type>() << '\n';
	show_format(buffer, value, std::chars_format::scientific);
	show_format(buffer, value, std::chars_format::fixed);
	show_format(buffer, value, std::chars_format::hex);
	show_format(buffer, value, std::chars_format::general);
	std::cout << '\n';
}

}	//	test

int main()
{
	using T = std::float128_t;
	using L = std::numeric_limits<T>;

	constexpr T
		//value = -L::quiet_NaN();
		value = -35896.53987658756543653653365436f128;

	test::show(value);
	test::show(boost::float80_t(value));
	test::show(std::float64_t(value));
	test::show(std::float32_t(value));
	test::show(std::float16_t(value));
	test::show(std::bfloat16_t(value));
	
	return EXIT_SUCCESS;
}
