// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/math/common.h
/// \brief \ref fennec_math_common
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///


#ifndef FENNEC_MATH_COMMON_H
#define FENNEC_MATH_COMMON_H


///
///
///
/// \page fennec_math_common Common
///
/// \brief The Common Functions defined in the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf).
///
/// \code #include <fennec/math/common.h> \endcode
///
///
///
/// \section fennec_math_common_section_sign_functions Sign Functions
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_sign_functions">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::abs "genIType abs(genIType x)" <br>
///         \ref fennec::abs "genFType abs(genFType x)" <br>
///         \ref fennec::abs "genDType abs(genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::abs
///
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::sign "genIType sign(genIType x)" <br>
///         \ref fennec::sign "genFType sign(genFType x)" <br>
///         \ref fennec::sign "genDType sign(genDType x)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::sign
///
/// </table>
///
///
/// \section fennec_math_common_section_rounding_functions Rounding Functions
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_rounding_functions">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::floor "genFType floor(genFType x)" <br>
///         \ref fennec::floor "genDType floor(genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::floor
///
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::ceil "genFType ceil(genFType x)" <br>
///         \ref fennec::ceil "genDType ceil(genDType x)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::ceil
///
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::round "genFType round(genFType x)" <br>
///         \ref fennec::round "genDType round(genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::round
///
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::roundEven "genFType roundEven(genFType x)" <br>
///         \ref fennec::roundEven "genDType roundEven(genDType x)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::roundEven
///
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::trunc "genFType trunc(genFType x)" <br>
///         \ref fennec::trunc "genDType trunc(genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::trunc
///
/// </table>
///
///
/// \section fennec_math_common_section_decimal_functions Decimal-Point Functions
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_decimal_functions">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::fract "genFType fract(genFType x)" <br>
///         \ref fennec::fract "genDType fract(genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::fract
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::mod "genFType mod(genFType x, float y)"    <br>
///         \ref fennec::mod "genFType mod(genFType x, genFType y)" <br>
///         \ref fennec::mod "genDType mod(genDType x, double y)"   <br>
///         \ref fennec::mod "genDType mod(genDType x, genDType y)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::mod
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::modf "genFType modf(genFType x, out genFType i)" <br>
///         \ref fennec::modf "genDType modf(genDType x, out genDType i)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::modf
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::isnan "genBType isnan(genFType x)" <br>
///         \ref fennec::isnan "genBType isnan(genDType x)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::isnan
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::isinf "genBType isinf(genFType x)" <br>
///         \ref fennec::isinf "genBType isinf(genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::isinf
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::frexp "genFType frexp(genFType x, out genIType exp)" <br>
///         \ref fennec::frexp "genDType frexp(genDType x, out genIType exp)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::frexp
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::ldexp "genFType ldexp(genFType x, genIType exp)" <br>
///         \ref fennec::ldexp "genDType ldexp(genDType x, genIType exp)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::ldexp
///
/// </table>
///
///
/// \section fennec_math_common_section_bit_functions Bit Conversion
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_bit_conversions">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::floatBitsToInt  "genIType floatBitsToInt(genType value)" <br>
///         \ref fennec::floatBitsToUint "genUType floatBitsToUint(genType value)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::floatBitsToUint
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::intBitsToFloat  "genFType intBitsToFloat(genIType value)" <br>
///         \ref fennec::uintBitsToFloat "genFType uintBitsToFloat(genUType value)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::uintBitsToFloat
///
/// </table>
///
///
/// \section fennec_math_common_section_comparison_functions Comparison Functions
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_comparison_functions">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::min "genFType min(genFType x, float y)"    <br>
///         \ref fennec::min "genFType min(genFType x, genFType y)" <br>
///         \ref fennec::min "genDType min(genDType x, double y)"   <br>
///         \ref fennec::min "genDType min(genDType x, genDType y)" <br>
///         \ref fennec::min "genIType min(genDType x, int y)"      <br>
///         \ref fennec::min "genIType min(genIType x, genIType y)" <br>
///         \ref fennec::min "genUType min(genUType x, uint y)"     <br>
///         \ref fennec::min "genUType min(genUType x, genUType y)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::min
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::max "genFType max(genFType x, float y)"    <br>
///         \ref fennec::max "genFType max(genFType x, genFType y)" <br>
///         \ref fennec::max "genDType max(genDType x, double y)"   <br>
///         \ref fennec::max "genDType max(genDType x, genDType y)" <br>
///         \ref fennec::max "genIType max(genDType x, int y)"      <br>
///         \ref fennec::max "genIType max(genIType x, genIType y)" <br>
///         \ref fennec::max "genUType max(genUType x, uint y)"     <br>
///         \ref fennec::max "genUType max(genUType x, genUType y)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::max
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::clamp "genFType clamp(genFType x, float minVal, float maxVal)"       <br>
///         \ref fennec::clamp "genFType clamp(genFType x, genFType minVal, genFType maxVal)" <br>
///         \ref fennec::clamp "genDType clamp(genDType x, double minVal, double maxVal)"     <br>
///         \ref fennec::clamp "genDType clamp(genDType x, genDType minVal, genDType maxVal)" <br>
///         \ref fennec::clamp "genIType clamp(genDType x, int minVal, int maxVal)"           <br>
///         \ref fennec::clamp "genIType clamp(genIType x, genIType minVal, genIType maxVal)" <br>
///         \ref fennec::clamp "genUType clamp(genUType x, genUType minVal, uint maxVal)"     <br>
///         \ref fennec::clamp "genUType clamp(genUType x, genUType minVal, genUType maxVal)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::clamp
///
/// </table>
///
///
/// \section fennec_math_common_section_curve_functions Curve Functions
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_curve_functions">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::step "step(float edge, genFType x)"    <br>
///         \ref fennec::step "step(genFType edge, genFType x)" <br>
///         \ref fennec::step "step(double edge, genDType x)"   <br>
///         \ref fennec::step "step(genFType edge, genDType x)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::step
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::smoothstep "smoothstep(float edge0, float edge1, genFType x)"       <br>
///         \ref fennec::smoothstep "smoothstep(genFType edge0, genFType edge1, genFType x)" <br>
///         \ref fennec::smoothstep "smoothstep(double edge0, double edge1, genDType x)"     <br>
///         \ref fennec::smoothstep "smoothstep(genFType edge0, genFType edge1, genDType x)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::smoothstep
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::mix "mix(genFType x, genFType y, float a)"    <br>
///         \ref fennec::mix "mix(genFType x, genFType y, genFType a)" <br>
///         \ref fennec::mix "mix(genDType x, genDType y, double a)"  <br>
///         \ref fennec::mix "mix(genDType x, genDType y, genDType a)"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::mix
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
///         \ref fennec::mix "mix(genBType x, genBType y, genBType a)" <br>
///         \ref fennec::mix "mix(genIType x, genIType y, genBType a)" <br>
///         \ref fennec::mix "mix(genUType x, genUType y, genBType a)" <br>
///         \ref fennec::mix "mix(genFType x, genFType y, genBType a)" <br>
///         \ref fennec::mix "mix(genDType x, genDType y, genBType a)"
///     <td width="50%" style="vertical-align: top" class="odd_c">
///         \copydetails fennec::mix
///
/// </table>
///
///
///

#include <fennec/math/detail/_math.h>

#include <fennec/lang/limits.h>

#include <fennec/math/vector.h>

#if FENNEC_COMPILER_MSVC
#define isnanf(x) isnan(x)
#define isinff(x) isinf(x)
#endif

namespace fennec
{


// Sign Functions ======================================================================================================


/// \name Sign Related Functions
/// @{



// Sign ================================================================================================================

///
/// \brief Returns \math{1} if \math{x > 0}, \math{0} if \math{x = 0}, or \math{-1} if \math{x<0}
///
/// \returns \math{1} if \math{x > 0}, \math{0} if \math{x = 0}, or \math{-1} if \math{x<0} <br><br>
/// \details We can express this as, <br><br>
///          \math{\text{sign}(x) = \text{sgn}(x) = \left\{\begin{array}{lr} -1 & x < 0, \\ 0 & x = 0, \\ 1 & x > 0.\end{array}\right.} <br><br>
///
/// \param x input value
template<typename genType>
constexpr genType sign(genType x) {
	return (x < genType(0) ? genType(-1) : genType(1)) * static_cast<genType>(x != 0); // reduces to cmove
}


// Absolute Value ======================================================================================================

///
/// \brief Returns \math{x} if \math{x \ge 0}, otherwise returns \math{-x}
///
/// \returns \math{x} if \math{x \ge 0}, otherwise it returns \math{-x}. <br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{abs}(x)=\left|x\right|}.<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType abs(genType x) {
	return x * fennec::sign(x);
}

/// @}



// Rounding Functions ==================================================================================================


/// \name Rounding Functions
/// @{


// Floor ===============================================================================================================

///
/// \brief Returns a value equal to the nearest integer that is less than or equal to \math{x}
///
/// \returns a value equal to the nearest integer that is less than or equal to \math{x} <br> <br>
/// \details We can express this as,<br> <br>
///          \math{\text{floor}(x)=\lfloor x\rfloor}<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType floor(genType x) {
	return ::floor(x);
}



// Ceil ================================================================================================================

///
/// \brief Returns a value equal to the nearest integer that is greater than or equal to \math{x}
///
/// \returns a value equal to the nearest integer that is greater than or equal to \math{x} <br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{ceil}(x)=\lceil{x}\rceil} <br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType ceil(genType x) {
	return ::ceil(x);
}



// Round ===============================================================================================================

///
/// \brief Returns a value equal to the nearest integer. In C++, a fractional part of \math{0.5} will always round up.
///
/// \returns a value equal to the nearest integer.<br> <br>
/// \details In C++, a fractional part of \math{0.5} will always round up.<br> <br>
///          We can express this as, <br> <br>
///          \math{\text{round}(x) = \text{sgn}(x) \cdot \lfloor \left| x \right| + 0.5 \rfloor}<br> <br>
///
/// \param x input value
template<typename genType> constexpr genType round(genType x) {
	return ::round(x);
}



// Trunc ===============================================================================================================

///
/// \brief Returns a value equal to the nearest integer that is less than or equal to \math{x}
///
/// \returns a value equal to the nearest integer that is less than or equal to \math{x} <br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{trunc}(x) = \text{sgn}(x) \cdot \lceil \left| x \right| - 0.5 \rceil}<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType trunc(genType x) {
	return ::trunc(x);
}



// Round Even ==========================================================================================================

///
/// \brief Returns a value equal to the nearest integer. In C++, a fractional part of \math{0.5} will always
///        round to the nearest even integer.
///
/// \returns a value equal to the nearest integer.<br> <br>
/// \details In C++, a fractional part of \math{0.5} will always round to the nearest even integer.<br> <br>
///          We can express this as,<br> <br>
///          \math{\text{roundEven}() = \begin{cases}\lfloor{x}\rfloor + \text{mod}(\lfloor{x}\rfloor, 2.0) & \text{fract}(x) = 0.5, \\ \text{round}(x) \end{cases}}<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType roundEven(genType x) {
	static const genType e = numeric_limits<genType>::epsilon();
	int I = static_cast<int>(x);
	genType i = static_cast<genType>(I);
	genType f = x - i;
	if (abs(f - genType(0.5)) > e)
		return fennec::round(x);

	bool dir = (I % 2);
	return dir ? i + 1 : i;

	// Older version that generates significantly more branching and asm instructions
	//const genType e = numeric_limits<genType>::epsilon();
	//genType f = x - fennec::floor(x);
	//if (fennec::abs(f - genType(0.5)) > e)
	//	return fennec::round(x);
	//
	//genType i = fennec::floor(x);
	//genType r = i / 2;
	//bool up = r - fennec::floor(r) > e; // This will cause a branch in most circumstances
	//return i + static_cast<genType>(up);
}

/// @}


// Floating Point Functions ============================================================================================


/// \name Floating Point Functions
/// @{


// Fract ===============================================================================================================

///
/// \brief Returns \math{x - floor(x)}
///
/// \returns \math{x - \text{floor}(x)} <br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{fract}(x)=x-\text{floor}}<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType fract(genType x) {
	return x - ::floor(x);
}


// Mod =================================================================================================================

///
/// \brief Modulus. Returns \math{x-y\cdot floor (x/y)}
///
/// \returns \math{x-y\cdot\text{floor}(x/y)} <br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{fract}(x)=x-\text{floor}(\frac{x}{y})}<br> <br>
///
/// \param x dividend
/// \param y divisor
template<typename genType>
constexpr genType mod(genType x, genType y) {
	return x - y * fennec::floor(x / y);
}


// ModF ================================================================================================================

///
/// \brief Returns the fractional part of \math{x} and stores the integral part in \math{i}.
///
/// \returns the fractional part of \math{x} and stores the integral part in \math{i}. <br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{modf}(x) = \text{trunc}(x),\, i := \text{fract}(x)} <br> <br>
///
/// \param x input value
/// \param i integral out
template<typename genType>
constexpr genType modf(genType x, genType& i) {
	i = fennec::floor(x); return fennec::fract(x);
}


// Is NaN ==============================================================================================================

///
/// \brief Returns **true** if \math{x} holds a NaN. Returns **false** otherwise.
///
/// \returns **true** if \math{x} holds a NaN. Returns **false** otherwise. <br> <br>
/// \details \math{NaN} is a concept unique to computing. It strictly means, and more specifically,
///          floating point values can only represent *real* numbers. This is why some functions,
///          like \ref fennec::sqrt "sqrt()", return \math{NaN} when an expression would return
///          a value in a different coordinate space. There are other cases, such as \math{\frac{1}{x}},
///          where \math{x=0} is undefined, and respectively the return value is also \math{NaN}. <br> <br>
///
///          To learn more, see [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) <br> <br>
///
/// \param x input value
template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType>)
constexpr genBType isnan(genType x) {
	return ::isnanf(x);
}


// Is Inf ==============================================================================================================

///
/// \brief Returns **true** if \math{x} holds a positive or negative infinity. Returns **false** otherwise. <br> <br>
///
/// \returns **true** if \math{x} holds a positive or negative infinity. Returns **false** otherwise.
/// \details \math{\inf}, or \math{\infty}, is used to express any function that is boundless, endless, or larger
///          than any natural number. This function has applications in Set Theory and Mathematical Analysis. <br> <br>
///
/// \param x input value
template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType>)
constexpr genBType isinf(genType x) {
	return ::isinff(x);
}



// Bit Conversion ======================================================================================================

///
/// \copydetails fennec::floatBitsToUint
template<typename genType, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr genIType floatBitsToInt(genType x) {
	return fennec::bit_cast<genIType>(x);
}


///
/// \brief Returns a signed or unsigned integer value representing the encoding of a floating-point value.
///        The float value's bit-level representation is preserved.
///
/// \returns a signed or unsigned integer value representing the encoding of a floating-point value.
///          The float value's bit-level representation is preserved. <br> <br>
/// \details we can express this in set theory, i.e. <br> <br>
///          let   \math{B} be a set, such that, \math{B=\left\lbrace{b_{0},...b_{32}}\right\rbrace} and \math{b_{i} \in S} where \math{S=\left\lbrace{0, 1}\right\rbrace}<br>
///          let   \math{m_B=\frac{b_{0}}{2^1}+\cdots +\frac{b_{i}}{2^{i+1}}+\cdots +\frac{b_{22}}{2^{23}}\begin{cases}1&\end{cases}}<br>
///          let   \math{p_B=b_{23}\cdot 2^0+\cdots +b_i\cdot 2^{i-23}+\cdots +b_{30}\cdot 2^7-127}<br>
///          let   \math{s_B=\begin{cases}-1,&b_{31}=1 \\ 1\end{cases}}<br> <br>
///          then, \math{x_B=s_B m_B 2^{p_B}}<br>
///          and,  \math{i_B=-b_{31} 2^{31}+\sum_{i=0}^{30}{a_i 2^i}}<br>
///          and,  \math{u_B=\sum_{i=0}^{31}{a_i 2^i}}<br> <br>
///
/// \param x value to convert
template<typename genType, typename genUType = uint_t> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr genUType floatBitsToUint(genType x) {
	return fennec::bit_cast<genUType>(x);
}


///
/// \copydetails fennec::uintBitsToFloat
template<typename genType = float_t, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr genType intBitsToFloat(genIType x) {
	return fennec::bit_cast<genType>(x);
}


///
/// \brief Returns a floating-point value corresponding to a signed or unsigned integer encoding of a floating-point value. <br> <br>
///
/// \returns a floating-point value corresponding to a signed or unsigned integer encoding of a floating-point value. <br> <br>
/// \details we can express this in set theory, i.e. <br> <br>
///          let   \math{B} be a set, such that, \math{B=\left\lbrace{b_{0},...b_{32}}\right\rbrace} and \math{b_{i} \in S} where \math{S=\left\lbrace{0, 1}\right\rbrace}<br>
///          let   \math{m_B=\frac{b_{0}}{2^1}+\cdots +\frac{b_{i}}{2^{i+1}}+\cdots +\frac{b_{22}}{2^{23}}\begin{cases}1&\end{cases}}<br>
///          let   \math{p_B=b_{23}\cdot 2^0+\cdots +b_i\cdot 2^{i-23}+\cdots +b_{30}\cdot 2^7-127}<br>
///          let   \math{s_B=\begin{cases}-1,&b_{31}=1 \\ 1\end{cases}}<br> <br>
///          then, \math{x_B=s_B m_B 2^{p_B}}<br>
///          and,  \math{i_B=-b_{31} 2^31+\sum_{i=0}^{30}{a_i 2^i}}<br>
///          and,  \math{u_B=\sum_{i=0}^{31}{a_i 2^i}}<br> <br>
///
/// \param x value to convert
template<typename genType = float_t, typename genUType = uint_t> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr genType uintBitsToFloat(genUType x) {
	return fennec::bit_cast<genType>(x);
}



// fma =================================================================================================================

///
/// \brief Computes and returns \math{a \cdot b + c}. <br> <br>
///
/// \returns \math{a \cdot b + c}.
/// \details In C++, this function will use the [fused multiply-add](https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation),
///          when the instruction is present on the target architecture. <br> <br>
///
/// \param a the multiplicand
/// \param b the multiplier
/// \param c the addend
template<typename genType>
constexpr genType fma(genType a, genType b, genType c) {
	return genType(::fma(a, b, c));
}



// frexp ===============================================================================================================

///
/// \brief Splits \math{x} into a floating-point significand in the range \math{[0.5,1.0]}, and an integral exponent of two,
/// such that \math{x=sig \cdot 2^{exp}}.
///
/// \returns The significand of the expression.<br> <br>
/// \details The significand is returned by the function and the exponent is returned in the parameter \math{exp}.
///          For a floating-point value of zero, the significand and exponent are both zero. If an implementation
///          supports signed zero, an input value of minus zero should return a significand of minus zero. For a
///          floating-point value that is an infinity or is not a number, the results are undefined. If the input
///          \math{x} is a vector, this operation is performed in a component-wise manner; the value returned by the
///          function and the value written to \math{exp} are vectors with the same number of components as \math{x}. <br> <br>
///
/// \param x The floating-point value to split
/// \param exp The variable to store the exponent in
template<typename genType, typename genIType = int_t> requires(is_integral_v<genIType>)
constexpr genType frexp(genType x, genIType& exp) {
	return ::frexp(x, &exp);
}



// ldexp ===============================================================================================================

///
/// \brief Builds a floating-point number from \math{x} and the corresponding integral exponent of two in \math{exp}
///
/// \returns \math{{x}\cdot{2^{exp}}}
/// \details Builds a floating-point number from x and the corresponding integral exponent of two in exp,
///          returning: \math{{x}\cdot{2^{exp}}}. If this product is too large to be represented in the floating-point
///          type, the result is undefined. If exp is greater than +128 (single-precision) or +1024 (double-precision),
///          the value returned is undefined. If exp is less than -126 (single-precision) or -1022 (double-precision),
///          the value returned may be flushed to zero. Additionally, splitting the value into a significand and
///          exponent using frexp() and then reconstructing a floating-point value using ldexp() should yield
///          the original input for zero and all finite non-subnormal values. If the input x is a vector, this
///          operation is performed in a component-wise manner; the value passed in exp and returned by the
///          function are vectors with the same number of components as x.
///
/// \param x The significand
/// \param exp The exponent
template<typename genType, typename genIType = int_t> requires(is_integral_v<genIType>)
constexpr genType ldexp(genType x, genIType exp) {
	return ::ldexp(x, exp);
}


/// @}


// Comparison Functions ================================================================================================


/// \name Comparison Functions
/// @{


// Min =================================================================================================================

///
/// \brief Returns \math{y} if \math{x<y;} otherwise it returns \math{x}
///
/// \returns \math{y} if \math{x<y}, otherwise it returns \math{x}<br> <br>
/// \details We can express this as,<br> <br>
///          \math{\text{min}(x, y)=\begin{cases}y, & y < x \\ x\end{cases}}<br> <br>
///
/// \param x input value \math{x}
/// \param y input value \math{y}
template<typename genType>
constexpr genType min(genType x, genType y) {
	return (y < x) ? y : x;
}


// Max =================================================================================================================

///
/// \brief Returns \math{y} if \math{y<x}, otherwise it returns \math{x}
///
/// \returns \math{y} if \math{y<x}, otherwise it returns \math{x}<br> <br>
/// \details We can express this as,<br> <br>
///          \math{\text{max}(x, y)=\begin{cases}y, & x < y \\ x\end{cases}}<br> <br>
///
/// \param x first input value
/// \param y second input value
template<typename genType>
constexpr genType max(genType x, genType y) {
	return (x < y) ? y : x;
}


// Clamp ===============================================================================================================

///
/// \brief Returns \math{min (max (x, minVal), maxVal)}
///
/// \returns \math{\text{min}(\text{max}(x, minVal), maxVal)}. Results are undefined if \math{minVal > maxVal}<br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{clamp}(x, min, max)=\begin{cases}min, & x < min \\ x \\ max, & x > max\end{cases}}<br> <br>
///
/// \param x input value
/// \param minVal minimum value
/// \param maxVal maximum value
template<typename genType>
constexpr genType clamp(genType x, genType minVal, genType maxVal) {
	return min(max(x, minVal), maxVal);
}


/// @}


// Curves ==============================================================================================================


/// \name Simple Curves
/// @{


// Step ================================================================================================================

///
/// \brief Returns \math{0.0} if \math{x<edge}, otherwise, it returns \math{1.0}
///
/// \returns \math{0.0} if \math{x<edge}, otherwise, it returns \math{1.0}<br> <br>
/// \details We can express this as, <br> <br>
///          \math{\text{step}(edge, x)=\begin{cases}0.0 & x < edge \\ 1.0 \end{cases}}<br> <br>
///
/// \param edge The \math{x} coordinate of the discontinuity
/// \param x The coordinate of the sample location
template<typename genType> requires(is_floating_point_v<genType>)
constexpr genType step(genType edge, genType x) {
	return static_cast<genType>(not(x < edge));
}


// Smoothstep ==========================================================================================================

///
/// \brief Returns \math{0.0} if \math{x\le edge0} and \math{1.0} if \math{x\ge edge1}, and performs smooth Hermite
///        interpolation between \math{0} and \math{1} when \math{edge0<x<edge1}.
///
/// \returns \math{0.0} if \math{x\le edge0} and \math{1.0} if \math{x\ge edge1}, and performs smooth Hermite
///          interpolation between \math{0} and \math{1} when \math{edge0<x<edge1}. <br> <br>
/// \details This is useful in cases where you would want a threshold function with a smooth transition.<br> <br>
///          This is equivalent to: <br> <br>
///          \code{.glsl}
///          genFType t;
///          t = clamp((x - edge0) / (edge1 - edge0), 0, 1);
///          return t * t * (3 - 2 * t);
///          \endcode<br>
///          Results are undefined if \math{edge0\ge edge1}. <br> <br>
///          We can express this as,<br> <br>
///          \math{\text{smoothstep}(e_0, e_1, x)=\begin{cases}0, & {x}\le{e_0} \\ 3{\frac{x-e_0}{e_1-e_0}}^{2} - 2{\frac{x-e_0}{e_1-e_0}}^{3}, & {e_0}\le{x}\le{e_1} \\ 1, & {1}\le{e_x}\end{cases}}<br> <br>
///
/// \param edge0 \math{x} value where the function returns \math{0.0}
/// \param edge1 \math{x} value where the function returns \math{1.0}
/// \param x \math{x} coordinate input
template<typename genType> requires(is_floating_point_v<genType>)
constexpr genType smoothstep(genType edge0, genType edge1, genType x) {
	genType t = fennec::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); return t * t * (3 - 2 * t);
}


// Mix =================================================================================================================

///
/// \brief Returns the linear blend of \math{x} and \math{y}, i.e., \math{x \cdot (1-a) + y \cdot a}
///
/// \returns the linear blend of \math{x} and \math{y}, i.e., \math{x \cdot (1-a) + y \cdot a}<br> <br>
/// \details We can express this as,<br> <br>
///          \math{\text{mix}(x, y, a)=x+a \cdot (y - x)}<br> <br>
///          The reason for the difference between the mathematical definition and the glsl definition is
///          due to floating-point precision errors. Multiplying \math{x} and \math{y} separately preserves
///          their precision before the addition happens.
///
/// \param x First value
/// \param y Second value
/// \param a Interpolant
template<typename genType> requires(is_floating_point_v<genType>)
constexpr genType mix(genType x, genType y, genType a) {
	return x * (genType(1.0) - a) + y * a;
}


// Mix (Bool) ==========================================================================================================

///
/// \brief Selects which value to return. For Vectors, when the boolean is a scalar, it selects the vector, otherwise, each component is selected.
///
/// \returns \math{x} when \math{a=T}, otherwise returns \math{y}
/// \details Selects which value to return. For Vectors, when the boolean is a scalar,
///          it selects the vector, otherwise, each component is selected. <br> <br>
///          This implementation uses the ternary operator:<br>
///          \code{.cpp}return a ? x : y;\endcode
///          Which will get reduced down to a conditional move instruction over branching.<br> <br>
///          We can express this as,<br> <br>
///          \math{\text{mix}(x, y, A) = \begin{cases} x, & A=T \\ y & A=F \end{cases}}<br> <br>
///
/// \param x True Value
/// \param y False Value
/// \param a Boolean Value
template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr genType mix(genType x, genType y, genBType a) {
	return a ? y : x;
}


// Internal ============================================================================================================

#ifndef FENNEC_DOXYGEN
template<typename genType, size_t...i>
constexpr vector<genType, i...> sign(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::sign(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> abs(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::abs(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> floor(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::floor(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> ceil(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::ceil(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> round(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::round(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> trunc(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::trunc(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::roundEven(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> fract(const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::fract(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, genType y) {
	return x - y * fennec::floor(x / y);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, const vector<genType, i...>& y) {
	return x - y * fennec::floor(x / y);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genType, i...>& I) {
	I = fennec::floor(x); return fennec::fract(x);
}

template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isnan(const vector<genType, i...>& x) {
	return vector<genBType, i...>(fennec::isnan(x[i]) ...);
}

template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isinf(const vector<genType, i...>& x) {
	return vector<genBType, i...>(fennec::isinf(x[i]) ...);
}

template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genIType, i...> floatBitsToInt(const vector<genType, i...>& x) {
	return vector<genIType, i...>(fennec::bit_cast<genIType>(x[i])...);
}

template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genUType, i...> floatBitsToUint(const vector<genType, i...>& x) {
	return vector<genUType, i...>(fennec::bit_cast<genUType>(x[i])...);
}

template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genType, i...> intBitsToFloat(const vector<genIType, i...>& x) {
	return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}

template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genType, i...> uintBitsToFloat(const vector<genUType, i...>& x) {
	return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> fma(const vector<genType, i...>& a, const vector<genType, i...>& b, const vector<genType, i...>& c) {
	return vector<genType, i...>(fennec::fma(a[i], b[i], c[i]) ...);
}

template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> frexp(const vector<genType, i...>& x, vector<genIType, i...>& exp) {
	return vector<genType, i...>(fennec::frexp(x[i], exp[i])...);
}

template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> ldexp(const vector<genType, i...>& x, const vector<genIType, i...>& exp) {
	return vector<genType, i...>(fennec::ldexp(x[i], exp[i])...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> min(genType x, const vector<genType, i...>& y) {
	return vector<genType, i...>(fennec::min(x, y[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, genType y) {
	return vector<genType, i...>(fennec::min(x[i], y) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, const vector<genType, i...>& y) {
	return vector<genType, i...>(fennec::min(x[i], y[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> max(genType x, const vector<genType, i...>& y) {
	return vector<genType, i...>(fennec::max(x, y[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, genType y) {
	return vector<genType, i...>(fennec::max(x[i], y) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, const vector<genType, i...>& y) {
	return vector<genType, i...>(fennec::max(x[i], y[i]) ...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, genType minVal, genType maxVal) {
	return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal), maxVal)...);
}

template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, const vector<genType, i...>& minVal, const vector<genType, i...>& maxVal) {
	return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal[i]), maxVal[i])...);
}

template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(genType edge, const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::step(edge, x[i]) ...);
}

template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(const vector<genType, i...>& edge, const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::step(edge[i], x[i]) ...);
}

template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(genType edge0, genType edge1, const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::smoothstep(edge0,    edge1,    x[i]) ...);
}

template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(const vector<genType, i...>& edge0, const vector<genType, i...>& edge1, const vector<genType, i...>& x) {
	return vector<genType, i...>(fennec::smoothstep(edge0[i], edge1[i], x[i]) ...);
}

template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genType a) {
	return x * (genType(1.0) - a) + y * a;
}

template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genType, i...>& a) {
	return x * (genType(1.0) - a) + y * a;
}

template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genBType a) {
	return genDType((a ? y[i] : x[i])...);
}


template<typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> mix(const vector<genBType, i...>& x, const vector<genBType, i...>& y, const vector<genBType, i...>& a) {
	return genBType((a[i] ? y[i] : x[i])...);
}

template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genBType, i...>& a) {
	return genDType((a[i] ? y[i] : x[i])...);
}
#endif // FENNEC_DOXYGEN


/// @}


}

#endif // FENNEC_MATH_COMMON_H
