/*
	This file is part of Fennix Kernel.

	Fennix Kernel is free software: you can redistribute it and/or
	modify it under the terms of the GNU General Public License as
	published by the Free Software Foundation, either version 3 of
	the License, or (at your option) any later version.

	Fennix Kernel is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include <cfloat>
#include <type_traits>

namespace std
{
	constexpr double sin(double x)
	{
		const int NUM_TERMS = 10;

		double result = 0.0;
		double term = x;

		for (int i = 1; i <= NUM_TERMS; ++i)
		{
			result += term;
			term *= -x * x / ((2 * i) * (2 * i + 1));
		}

		return result;
	}

	constexpr float powf(float base, float exp)
	{
		float result = 1.0;
		for (int i = 0; i < (int)exp; ++i)
			result *= base;
		return result;
	}

	constexpr double pow(double base, double exp)
	{
		double result = 1.0;
		for (int i = 0; i < (int)exp; ++i)
			result *= base;
		return result;
	}

	constexpr long double powl(long double base, long double exp)
	{
		long double result = 1.0;
		for (long i = 0; i < (long)exp; ++i)
			result *= base;
		return result;
	}

	constexpr float fabsf(float num)
	{
		if (num < 0)
			return -num;
		return num;
	}

	constexpr double fabs(double num)
	{
		if (num < 0)
			return -num;
		return num;
	}

	constexpr long double fabsl(long double num)
	{
		if (num < 0)
			return -num;
		return num;
	}

	template <class Integer>
	constexpr bool isinf(Integer num)
	{
		union
		{
			unsigned long u;
			double f;
		} ieee754;
		ieee754.f = num;

		bool a = ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000;
		bool b = ((unsigned)ieee754.u == 0);
		return a && b;
	}

	template <class Integer>
	constexpr bool isnan(Integer num)
	{
		return num != num;
	}

	template <class Integer>
	constexpr double fabs(Integer num)
	{
		return num < 0 ? -num : num;
	}

	template <class Integer>
	constexpr double remainder(Integer x, Integer y)
	{
		return x - (int)(x / y) * y;
	}

	template <class Integer>
	constexpr double copysign(Integer mag, Integer sgn)
	{
		return (sgn < 0) ? -mag : mag;
	}

	template <class Integer>
	constexpr bool signbit(Integer num)
	{
		return num < 0;
	}

	template <class Integer>
	constexpr double fmod(Integer x, Integer y)
	{
		double result = std::remainder(std::fabs(x), y = std::fabs(y));
		if (std::signbit(result))
			result += (double)y;
		return std::copysign(result, x);
	}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"

	template <class Integer>
	constexpr double ceil(Integer num)
	{
		// int i = (int)num;
		// return i + (i < num);

		double remainder = std::fmod((double)num, 1.0);
		return num >= 0 ? (remainder == 0 ? num : num + 1 - remainder) : num - remainder;
	}

#pragma GCC diagnostic pop

	template <class Integer>
	constexpr double trunc(Integer num)
	{
		if (std::isinf(num))
			return num;

		if (std::isnan(num))
			return num;

		return static_cast<int>(num);
	}

	template <class Integer>
	constexpr double exp(Integer num)
	{
		double result = 1.0;
		double term = 1.0;

		for (int i = 1; i <= 10; ++i)
		{
			term *= static_cast<double>(num) / i;
			result += term;
		}

		return result;
	}
}
