Files
fennec/include/fennec/math/common.h

998 lines
46 KiB
C++

// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright (C) 2025 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 common.h
/// \brief \ref fennec_math_common
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 section_sign_functions Sign
///
/// <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(fennec::genType) "genIType abs(genIType x)" <br>
/// \ref fennec::abs(fennec::genType) "genFType abs(genFType x)" <br>
/// \ref fennec::abs(fennec::genType) "genDType abs(genDType x)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::abs(fennec::genType)
///
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::sign(fennec::genType) "genIType sign(genIType x)" <br>
/// \ref fennec::sign(fennec::genType) "genFType sign(genFType x)" <br>
/// \ref fennec::sign(fennec::genType) "genDType sign(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::sign(fennec::genType)
///
/// </table>
///
///
/// \section section_rounding_functions Rounding
///
/// <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(fennec::genType) "genFType floor(genFType x)" <br>
/// \ref fennec::floor(fennec::genType) "genDType floor(genDType x)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::floor(fennec::genType)
///
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::ceil(fennec::genType) "genFType ceil(genFType x)" <br>
/// \ref fennec::ceil(fennec::genType) "genDType ceil(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::ceil(fennec::genType)
///
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::round(fennec::genType) "genFType round(genFType x)" <br>
/// \ref fennec::round(fennec::genType) "genDType round(genDType x)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::round(fennec::genType)
///
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::roundEven(fennec::genType) "genFType roundEven(genFType x)" <br>
/// \ref fennec::roundEven(fennec::genType) "genDType roundEven(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::roundEven(fennec::genType)
///
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::trunc(fennec::genType) "genFType trunc(genFType x)" <br>
/// \ref fennec::trunc(fennec::genType) "genDType trunc(genDType x)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::trunc(fennec::genType)
///
/// </table>
///
///
/// \section section_decimal_functions Decimal-Point
///
/// <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(fennec::genType) "genFType fract(genFType x)" <br>
/// \ref fennec::fract(fennec::genType) "genDType fract(genDType x)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::fract(fennec::genType)
///
/// <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(fennec::genType, fennec::genType&) "genFType modf(genFType x, out genFType i)" <br>
/// \ref fennec::modf(fennec::genType, fennec::genType&) "genDType modf(genDType x, out genDType i)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::modf(fennec::genType, fennec::genType&)
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::isnan(fennec::genType) "genBType isnan(genFType x)" <br>
/// \ref fennec::isnan(fennec::genType) "genBType isnan(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::isnan(fennec::genType)
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::isinf(fennec::genType) "genBType isinf(genFType x)" <br>
/// \ref fennec::isinf(fennec::genType) "genBType isinf(genDType x)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::isinf(fennec::genType)
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::frexp(fennec::genType, fennec::genIType&) "genFType frexp(genFType x, out genIType exp)" <br>
/// \ref fennec::frexp(fennec::genType, fennec::genIType&) "genDType frexp(genDType x, out genIType exp)"
/// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::frexp(fennec::genType, fennec::genIType&)
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::ldexp(fennec::genType, fennec::genIType) "genFType ldexp(genFType x, genIType exp)" <br>
/// \ref fennec::ldexp(fennec::genType, fennec::genIType) "genDType ldexp(genDType x, genIType exp)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::ldexp(fennec::genType, fennec::genIType)
///
/// </table>
///
///
/// \section section_bit_conversion_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(fennec::genType) "genIType floatBitsToInt(genType value)" <br>
/// \ref fennec::floatBitsToUint(fennec::genType) "genUType floatBitsToUint(genType value)"
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::floatBitsToUint(fennec::genType)
///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::intBitsToFloat(fennec::genIType) "genFType intBitsToFloat(genIType value)" <br>
/// \ref fennec::uintBitsToFloat(fennec::genUType) "genFType uintBitsToFloat(genUType value)"
/// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::uintBitsToFloat(fennec::genType)
///
/// </table>
///
///
/// \section section_comparison_functions Comparison
///
/// <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 section_curve_functions Curves
///
/// <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(fennec::genBType, fennec::genBType, fennec::genBType) "mix(genBType x, genBType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genIType x, genIType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genUType x, genUType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genFType x, genFType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "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/math/vector.h>
namespace fennec
{
// Sign Functions ======================================================================================================
/// \name Sign Related Functions
/// @{
// Absolute Value ======================================================================================================
///
/// \brief Returns \f$x\f$ if \f$x \ge 0\f$, otherwise it returns \f$-x\f$
///
/// \returns \f$x\f$ if \f$x \ge 0\f$, otherwise it returns \f$-x\f$. <br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{abs}(x)=\left|x\right|\f$.<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType abs(genType x)
{ return std::abs(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> abs(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::abs(x[i]) ...); }
// Sign ================================================================================================================
///
/// \brief Returns \f$1\f$ if \f$x > 0\f$, \f$0\f$ if \f$x = 0\f$, or \f$-1\f$ if \f$x<0\f$
///
/// \returns \f$1\f$ if \f$x > 0\f$, \f$0\f$ if \f$x = 0\f$, or \f$-1\f$ if \f$x<0\f$ <br><br>
/// \details We can express this as, <br><br>
/// \f$\text{sign}(x) = \text{sgn}(x) = \left\{\begin{array}{lr} -1 & x < 0, \\ 0 & x = 0, \\ 1 & x > 0.\end{array}\right.\f$ <br><br>
///
/// \param x input value
template<typename genType>
constexpr genType sign(genType x)
{ return (x < 0 ? -1 : (x > 0 ? +1 : 0)); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> sign(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::sign(x[i]) ...); }
/// @}
// Rounding Functions ==================================================================================================
/// \name Rounding Functions
/// @{
// Floor ===============================================================================================================
///
/// \brief Returns a value equal to the nearest integer that is less than or equal to \f$x\f$
///
/// \returns a value equal to the nearest integer that is less than or equal to \f$x\f$ <br> <br>
/// \details We can express this as,<br> <br>
/// \f$\text{floor}(x)=\lfloor x\rfloor\f$<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType floor(genType x)
{ return ::floor(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> floor(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::floor(x[i]) ...); }
// Ceil ================================================================================================================
///
/// \brief Returns a value equal to the nearest integer that is greater than or equal to \f$x\f$
///
/// \returns a value equal to the nearest integer that is greater than or equal to \f$x\f$ <br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{ceil}(x)=\lceil{x}\rceil\f$ <br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType ceil(genType x)
{ return ::ceil(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> ceil(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::ceil(x[i]) ...); }
// Round ===============================================================================================================
///
/// \brief Returns a value equal to the nearest integer. In C++, a fractional part of \f$0.5\f$ will always round up.
///
/// \returns a value equal to the nearest integer.<br> <br>
/// \details In C++, a fractional part of \f$0.5\f$ will always round up.<br> <br>
/// We can express this as, <br> <br>
/// \f$\text{round}(x) = \text{sgn}(x) \cdot \lfloor \left| x \right| + 0.5 \rfloor\f$<br> <br>
///
/// \param x input value
template<typename genType> constexpr genType round(genType x)
{ return ::round(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> round(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::round(x[i]) ...); }
// Round Even ==========================================================================================================
///
/// \brief Returns a value equal to the nearest integer. In C++, a fractional part of \f$0.5\f$ 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 \f$0.5\f$ will always round to the nearest even integer.<br> <br>
/// We can express this as,<br> <br>
/// \f$\text{roundEven}() = \begin{cases}\lfloor{x}\rfloor + \text{mod}(\lfloor{x}\rfloor, 2.0) & \text{fract}(x) = 0.5, \\ \text{round}(x) \end{cases}\f$<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType roundEven(genType x)
{ return x - floor(x) == 0.5f ? floor(x) + (static_cast<int>(floor(x)) % 2) : round(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::roundEven(x[i]) ...); }
// Trunc ===============================================================================================================
///
/// \brief Returns a value equal to the nearest integer that is less than or equal to \f$x\f$
///
/// \returns a value equal to the nearest integer that is less than or equal to \f$x\f$ <br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{trunc}(x) = \text{sgn}(x) \cdot \lceil \left| x \right| - 0.5 \rceil\f$<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType trunc(genType x)
{ return ::trunc(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> trunc(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::trunc(x[i]) ...); }
/// @}
// Floating Point Functions ============================================================================================
/// \name Floating Point Functions
/// @{
// Fract ===============================================================================================================
///
/// \brief Returns \f$x - floor(x)\f$
///
/// \returns \f$x - \text{floor}(x)\f$ <br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{fract}(x)=x-\text{floor}\f$<br> <br>
///
/// \param x input value
template<typename genType>
constexpr genType fract(genType x)
{ return x - ::floor(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> fract(const vector<genType, i...>& x)
{ return vector<genType, i...>(fennec::fract(x[i]) ...); }
// Mod =================================================================================================================
///
/// \brief Modulus. Returns \f$x-y\cdot floor (x/y)\f$
///
/// \returns \f$x-y\cdot\text{floor}(x/y)\f$ <br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{fract}(x)=x-\text{floor}(\frac{x}{y})\f$<br> <br>
///
/// \param x dividend
/// \param y divisor
template<typename genType>
constexpr genType mod(genType x, genType y)
{ return x - y * fennec::floor(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, genType y)
{ return x - y * fennec::floor(x); }
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); }
// ModF ================================================================================================================
///
/// \brief Returns the fractional part of \f$x\f$ and stores the integral part in \f$i\f$.
///
/// \returns the fractional part of \f$x\f$ and stores the integral part in \f$i\f$. <br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{modf}(x) = \text{trunc}(x),\, i := \text{fract}(x)\f$ <br> <br>
///
/// \param x input value
/// \param i integral out
template<typename genType>
constexpr genType modf(genType x, genType& i)
{ return ::modf(x, &i); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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); }
// Is NaN ==============================================================================================================
///
/// \brief Returns **true** if \f$x\f$ holds a NaN. Returns **false** otherwise.
///
/// \returns **true** if \f$x\f$ holds a NaN. Returns **false** otherwise. <br> <br>
/// \details \f$NaN\f$ 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(fennec::genFType) "sqrt()", return \f$NaN\f$ when an expression would return
/// a value in a different coordinate space. There are other cases, such as \f$\frac{1}{x}\f$,
/// where \f$x=0\f$ is undefined, and respectively the return value is also \f$NaN\f$. <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 ::isnan(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// Is Inf ==============================================================================================================
///
/// \brief Returns **true** if \f$x\f$ holds a positive or negative infinity. Returns **false** otherwise. <br> <br>
///
/// \returns **true** if \f$x\f$ holds a positive or negative infinity. Returns **false** otherwise.
/// \details \f$\inf\f$, or \f$\infty\f$, 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 ::isinf(x); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// Bit Conversion ======================================================================================================
///
/// \copydetails fennec::floatBitsToUint(fennec::genFType)
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(genIType 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 \f$B\f$ be a set, such that, \f$B=\left\lbrace{b_{0},...b_{32}}\right\rbrace\f$ and \f$b_{i} \in S\f$ where \f$S=\left\lbrace{0, 1}\right\rbrace\f$<br>
/// let \f$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}\f$<br>
/// let \f$p_B=b_{23}\cdot 2^0+\cdots +b_i\cdot 2^{i-23}+\cdots +b_{30}\cdot 2^7-127\f$<br>
/// let \f$s_B=\begin{cases}-1,&b_{31}=1 \\ 1\end{cases}\f$<br> <br>
/// then, \f$x_B=s_B m_B 2^{p_B}\f$<br>
/// and, \f$i_B=-b_{31} 2^{31}+\sum_{i=0}^{30}{a_i 2^i}\f$<br>
/// and, \f$u_B=\sum_{i=0}^{31}{a_i 2^i}\f$<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(fennec::genUType)
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 \f$B\f$ be a set, such that, \f$B=\left\lbrace{b_{0},...b_{32}}\right\rbrace\f$ and \f$b_{i} \in S\f$ where \f$S=\left\lbrace{0, 1}\right\rbrace\f$<br>
/// let \f$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}\f$<br>
/// let \f$p_B=b_{23}\cdot 2^0+\cdots +b_i\cdot 2^{i-23}+\cdots +b_{30}\cdot 2^7-127\f$<br>
/// let \f$s_B=\begin{cases}-1,&b_{31}=1 \\ 1\end{cases}\f$<br> <br>
/// then, \f$x_B=s_B m_B 2^{p_B}\f$<br>
/// and, \f$i_B=-b_{31} 2^31+\sum_{i=0}^{30}{a_i 2^i}\f$<br>
/// and, \f$u_B=\sum_{i=0}^{31}{a_i 2^i}\f$<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); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// fma =================================================================================================================
///
/// \brief Computes and returns \f$a \cdot b + c\f$. <br> <br>
///
/// \returns \f$a \cdot b + c\f$.
/// \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 ::fma(a, b, c); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// frexp ===============================================================================================================
///
/// \brief Splits \f$x\f$ into a floating-point significand in the range \f$[0.5,1.0]\f$, and an integral exponent of two,
/// such that \f$x=sig \cdot 2^{exp}\f$.
///
/// \returns The significand of the expression.<br> <br>
/// \details The significand is returned by the function and the exponent is returned in the parameter \f$exp\f$.
/// 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
/// \f$x\f$ is a vector, this operation is performed in a component-wise manner; the value returned by the
/// function and the value written to \f$exp\f$ are vectors with the same number of components as \f$x\f$. <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); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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])...); }
// ldexp ===============================================================================================================
///
/// \brief Builds a floating-point number from \f$x\f$ and the corresponding integral exponent of two in \f$exp\f$
///
/// \returns \f${x}\cdot{2^{exp}}\f$
/// \details Builds a floating-point number from x and the corresponding integral exponent of two in exp,
/// returning: \f${x}\cdot{2^{exp}}\f$. 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); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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<genType, i...>& exp)
{ return vector<genType, i...>(fennec::ldexp(x[i], exp[i])...); }
/// @}
// Comparison Functions ================================================================================================
/// \name Comparison Functions
/// @{
// Min =================================================================================================================
///
/// \brief Returns \f$y\f$ if \f$x<y;\f$ otherwise it returns \f$x\f$
///
/// \returns \f$y\f$ if \f$x<y\f$, otherwise it returns \f$x\f$<br> <br>
/// \details We can express this as,<br> <br>
/// \f$\text{min}(x, y)=\begin{cases}y, & y < x \\ x\end{cases}\f$<br> <br>
///
/// \param x input value \f$x\f$
/// \param y input value \f$y\f$
template<typename genType>
constexpr genType min(genType x, genType y)
{ return (y < x) ? y : x; }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// Max =================================================================================================================
///
/// \brief Returns \f$y\f$ if \f$y<x\f$, otherwise it returns \f$x\f$
///
/// \returns \f$y\f$ if \f$y<x\f$, otherwise it returns \f$x\f$<br> <br>
/// \details We can express this as,<br> <br>
/// \f$\text{max}(x, y)=\begin{cases}y, & x < y \\ x\end{cases}\f$<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; }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// Clamp ===============================================================================================================
///
/// \brief Returns \f$min (max (x, minVal), maxVal)\f$
///
/// \returns \f$\text{min}(\text{max}(x, minVal), maxVal)\f$. Results are undefined if \f$minVal > maxVal\f$<br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{clamp}(x, min, max)=\begin{cases}min, & x < min \\ x \\ max, & x > max\end{cases}\f$<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); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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])...); }
/// @}
// Curves ==============================================================================================================
/// \name Simple Curves
/// @{
// Step ================================================================================================================
///
/// \brief Returns \f$0.0\f$ if \f$x<edge\f$, otherwise, it returns \f$1.0\f$
///
/// \returns \f$0.0\f$ if \f$x<edge\f$, otherwise, it returns \f$1.0\f$<br> <br>
/// \details We can express this as, <br> <br>
/// \f$\text{step}(edge, x)=\begin{cases}0.0 & x < edge \\ 1.0 \end{cases}\f$<br> <br>
///
/// \param edge The \f$x\f$ 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 x < edge ? 0.0 : 1.0; }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// Smoothstep ==========================================================================================================
///
/// \brief Returns \f$0.0\f$ if \f$x\le edge0\f$ and \f$1.0\f$ if \f$x\ge edge1\f$, and performs smooth Hermite
/// interpolation between \f$0\f$ and \f$1\f$ when \f$edge0<x<edge1\f$.
///
/// \returns \f$0.0\f$ if \f$x\le edge0\f$ and \f$1.0\f$ if \f$x\ge edge1\f$, and performs smooth Hermite
/// interpolation between \f$0\f$ and \f$1\f$ when \f$edge0<x<edge1\f$. <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 \f$edge0\ge edge1\f$. <br> <br>
/// We can express this as,<br> <br>
/// \f$\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}\f$<br> <br>
///
/// \param edge0 \f$x\f$ value where the function returns \f$0.0\f$
/// \param edge1 \f$x\f$ value where the function returns \f$1.0\f$
/// \param x \f$x\f$ 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)); return t * t * (3 - 2 * t); }
// Vector Specializations ----------------------------------------------------------------------------------------------
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]) ...); }
// Mix =================================================================================================================
///
/// \brief Returns the linear blend of \f$x\f$ and \f$y\f$, i.e., \f$x \cdot (1-a) + y \cdot a\f$
///
/// \returns the linear blend of \f$x\f$ and \f$y\f$, i.e., \f$x \cdot (1-a) + y \cdot a\f$<br> <br>
/// \details We can express this as,<br> <br>
/// \f$\text{mix}(x, y, a)=x+a \cdot (y - x)\f$<br> <br>
/// The reason for the difference between the mathematical definition and the glsl definition is
/// due to floating-point precision errors. Multiplying \f$x\f$ and \f$y\f$ 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 * (1.0 - a) + y * a; }
// Vector Specializations ----------------------------------------------------------------------------------------------
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 * (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 * (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 \f$x\f$ when \f$a=T\f$, otherwise returns \f$y\f$
/// \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>
/// \f$\text{mix}(x, y, A) = \begin{cases} x, & A=T \\ y & A=F \end{cases}\f$<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; }
// Vector Specializations ----------------------------------------------------------------------------------------------
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_MATH_COMMON_H