From 6d6afa8b70a8c4b057b09a85708b743c5652bfa2 Mon Sep 17 00:00:00 2001 From: Toby Davis Date: Tue, 7 Nov 2023 12:46:35 +0000 Subject: [PATCH] Compile time improvements :) --- librapid/include/librapid/array/assignOps.hpp | 18 +- .../include/librapid/array/operations.hpp | 44 +- .../librapid/array/pseudoConstructors.hpp | 12 +- librapid/include/librapid/array/shape.hpp | 37 +- librapid/include/librapid/autodiff/dual.hpp | 26 +- librapid/include/librapid/math/complex.hpp | 47 +- librapid/include/librapid/math/constants.hpp | 210 ++-- librapid/include/librapid/math/coreMath.hpp | 910 +++++++++--------- .../librapid/math/utilityFunctions.hpp | 274 +++--- librapid/include/librapid/math/vector.hpp | 2 +- librapid/include/librapid/math/vectorImpl.hpp | 40 +- 11 files changed, 817 insertions(+), 803 deletions(-) diff --git a/librapid/include/librapid/array/assignOps.hpp b/librapid/include/librapid/array/assignOps.hpp index 765c15b2..9ce141f6 100644 --- a/librapid/include/librapid/array/assignOps.hpp +++ b/librapid/include/librapid/array/assignOps.hpp @@ -354,18 +354,14 @@ namespace librapid { #if defined(LIBRAPID_HAS_CUDA) namespace cuda { - template::type != - ::librapid::detail::LibRapidType::Scalar, - int> = 0> + template LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto dataSourceExtractor(const T &obj) { - return obj.storage().begin(); - } - - template::type == - ::librapid::detail::LibRapidType::Scalar, - int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const auto &dataSourceExtractor(const T &obj) { - return obj; + if constexpr(typetraits::TypeInfo::type == + ::librapid::detail::LibRapidType::Scalar) { + return obj; + } else { + return obj.storage().begin(); + } } template diff --git a/librapid/include/librapid/array/operations.hpp b/librapid/include/librapid/array/operations.hpp index 2df4b88e..7938e1a9 100644 --- a/librapid/include/librapid/array/operations.hpp +++ b/librapid/include/librapid/array/operations.hpp @@ -956,7 +956,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Cosine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto cos(VAL &&val) -> detail::Function, detail::Cos, VAL> { @@ -971,7 +971,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Tangent function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto tan(VAL &&val) -> detail::Function, detail::Tan, VAL> { @@ -986,7 +986,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Arcsine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto asin(VAL &&val) -> detail::Function, detail::Asin, VAL> { @@ -1001,7 +1001,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Arccosine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto acos(VAL &&val) -> detail::Function, detail::Acos, VAL> { @@ -1016,7 +1016,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Arctangent function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto atan(VAL &&val) -> detail::Function, detail::Atan, VAL> { @@ -1031,7 +1031,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Hyperbolic sine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto sinh(VAL &&val) -> detail::Function, detail::Sinh, VAL> { @@ -1046,7 +1046,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Hyperbolic cosine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto cosh(VAL &&val) -> detail::Function, detail::Cosh, VAL> { @@ -1061,7 +1061,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Hyperbolic tangent function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto tanh(VAL &&val) -> detail::Function, detail::Tanh, VAL> { @@ -1076,7 +1076,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Hyperbolic sine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto asinh(VAL &&val) -> detail::Function, detail::Asinh, VAL> { @@ -1091,7 +1091,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Hyperbolic cosine function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto acosh(VAL &&val) -> detail::Function, detail::Acosh, VAL> { @@ -1106,7 +1106,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Hyperbolic tangent function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto atanh(VAL &&val) -> detail::Function, detail::Atanh, VAL> { @@ -1121,7 +1121,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Exponential function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto exp(VAL &&val) -> detail::Function, detail::Exp, VAL> { @@ -1136,7 +1136,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Exponential function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto exp2(VAL &&val) -> detail::Function, detail::Exp2, VAL> { @@ -1151,7 +1151,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Exponential function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto exp10(VAL &&val) -> detail::Function, detail::Exp10, VAL> { @@ -1166,7 +1166,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Natural logarithm function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto log(VAL &&val) -> detail::Function, detail::Log, VAL> { @@ -1181,7 +1181,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Base 10 logarithm function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto log10(VAL &&val) -> detail::Function, detail::Log10, VAL> { @@ -1196,7 +1196,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Base 2 logarithm function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto log2(VAL &&val) -> detail::Function, detail::Log2, VAL> { @@ -1211,7 +1211,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Square root function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto sqrt(VAL &&val) -> detail::Function, detail::Sqrt, VAL> { @@ -1226,7 +1226,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Cube root function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto cbrt(VAL &&val) -> detail::Function, detail::Cbrt, VAL> { @@ -1241,7 +1241,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Absolute value function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto abs(VAL &&val) -> detail::Function, detail::Abs, VAL> { @@ -1256,7 +1256,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Floor function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto floor(VAL &&val) -> detail::Function, detail::Floor, VAL> { @@ -1271,7 +1271,7 @@ namespace librapid { /// \tparam VAL Type of the input /// \param val The input array or function /// \return Ceiling function object - template = 0> + template requires(detail::IsArrayOp) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto ceil(VAL &&val) -> detail::Function, detail::Ceil, VAL> { diff --git a/librapid/include/librapid/array/pseudoConstructors.hpp b/librapid/include/librapid/array/pseudoConstructors.hpp index e4214960..3f71f61a 100644 --- a/librapid/include/librapid/array/pseudoConstructors.hpp +++ b/librapid/include/librapid/array/pseudoConstructors.hpp @@ -51,8 +51,8 @@ namespace librapid { /// \tparam N Maximum number of dimensions of the Shape /// \param shape Shape of the Array /// \return Array filled with zeros - template::value, int> = 0> + template + requires(typetraits::IsSizeType::value) Array zeros(const ShapeType &shape) { return Array(shape, Scalar(0)); } @@ -79,8 +79,8 @@ namespace librapid { /// \tparam N Maximum number of dimensions of the Shape /// \param shape Shape of the Array /// \return Array filled with ones - template::value, int> = 0> + template + requires(typetraits::IsSizeType::value) Array ones(const ShapeType &shape) { return Array(shape, Scalar(1)); } @@ -109,8 +109,8 @@ namespace librapid { /// \tparam N Maximum number of dimensions of the Shape /// \param shape Shape of the Array /// \return Array filled with numbers from 0 to N-1 - template::value, int> = 0> + template + requires(typetraits::IsSizeType::value) Array ordered(const ShapeType &shape) { Array result(shape); for (size_t i = 0; i < result.size(); i++) { result.storage()[i] = Scalar(i); } diff --git a/librapid/include/librapid/array/shape.hpp b/librapid/include/librapid/array/shape.hpp index 207ccae9..5440413f 100644 --- a/librapid/include/librapid/array/shape.hpp +++ b/librapid/include/librapid/array/shape.hpp @@ -783,26 +783,10 @@ namespace librapid { /// \param second Second input /// \param shapes Remaining (optional) inputs /// \return True if all inputs have the same shape, false otherwise - template::value && - typetraits::IsSizeType::value && - (typetraits::IsSizeType::value && ...), - int> = 0> - LIBRAPID_NODISCARD LIBRAPID_INLINE bool shapesMatch(const First &first, const Second &second, - const Rest &...shapes) { - if constexpr (sizeof...(Rest) == 0) { - return first == second; - } else { - return first == second && shapesMatch(first, shapes...); - } - } - - /// \sa shapesMatch - template::value && - typetraits::IsSizeType::value && - (typetraits::IsSizeType::value && ...), - int> = 0> + template + requires(typetraits::IsSizeType::value && + typetraits::IsSizeType::value && + (typetraits::IsSizeType::value && ...)) LIBRAPID_NODISCARD LIBRAPID_INLINE bool shapesMatch(const std::tuple &shapes) { if constexpr (sizeof...(Rest) == 0) { @@ -813,6 +797,19 @@ namespace librapid { [](auto, auto, auto... rest) { return std::make_tuple(rest...); }, shapes)); } } + + /// \sa shapesMatch + // template + // requires(typetraits::IsSizeType::value && typetraits::IsSizeType::value && + // (typetraits::IsSizeType::value && ...)) + // LIBRAPID_NODISCARD LIBRAPID_INLINE bool shapesMatch(const First &first, const Second &second, + // const Rest &...shapes) { + // if constexpr (sizeof...(Rest) == 0) { + // return first == second; + // } else { + // return first == second && shapesMatch(first, shapes...); + // } + // } namespace detail { template diff --git a/librapid/include/librapid/autodiff/dual.hpp b/librapid/include/librapid/autodiff/dual.hpp index 15b50484..7ba0e749 100644 --- a/librapid/include/librapid/autodiff/dual.hpp +++ b/librapid/include/librapid/autodiff/dual.hpp @@ -2,9 +2,9 @@ #define LIBRAPID_AUTODIFF_DUAL #if defined(LIBRAPID_IN_JITIFY) -# define REQUIRE_SCALAR(TYPE) typename AD_ARG_ = void +# define IS_SCALAR(TYPE) false #else -# define REQUIRE_SCALAR(TYPE) typename std::enable_if_t, int> = 0 +# define IS_SCALAR(TYPE) std::is_scalar_v #endif // LIBRAPID_IN_JITIFY #if !defined(LIBRAPID_IN_JITIFY) @@ -119,13 +119,13 @@ namespace librapid { return {lhs.value + rhs.value, lhs.derivative + rhs.derivative}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator+(const Dual &lhs, const V &rhs) { return {lhs.value + rhs, lhs.derivative}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator+(const V &lhs, const Dual &rhs) { return {lhs + rhs.value, rhs.derivative}; @@ -137,13 +137,13 @@ namespace librapid { return {lhs.value - rhs.value, lhs.derivative - rhs.derivative}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator-(const Dual &lhs, const V &rhs) { return {lhs.value - rhs, lhs.derivative}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator-(const V &lhs, const Dual &rhs) { return {lhs - rhs.value, -rhs.derivative}; @@ -155,13 +155,13 @@ namespace librapid { return {lhs.value * rhs.value, lhs.derivative * rhs.value + lhs.value * rhs.derivative}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator*(const Dual &lhs, const V &rhs) { return {lhs.value * rhs, lhs.derivative * rhs}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator*(const V &lhs, const Dual &rhs) { return {lhs * rhs.value, lhs * rhs.derivative}; @@ -175,13 +175,13 @@ namespace librapid { (rhs.value * rhs.value)}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator/(const Dual &lhs, const V &rhs) { return {lhs.value / rhs, lhs.derivative / rhs}; } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual operator/(const V &lhs, const Dual &rhs) { return {lhs / rhs.value, -lhs * rhs.derivative / (rhs.value * rhs.value)}; @@ -328,14 +328,14 @@ namespace librapid { x.derivative / (3 * ::librapid::cbrt(x.value * x.value))); } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual pow(const Dual &x, const V &y) { using Ret = decltype(::librapid::pow(x.value, y)); return Dual(::librapid::pow(x.value, y), y * ::librapid::pow(x.value, y - 1) * x.derivative); } - template + template requires(IS_SCALAR(V)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Dual pow(const V &x, const Dual &y) { using Ret = decltype(::librapid::pow(x, y.value)); return Dual(::librapid::pow(x, y.value), @@ -464,4 +464,6 @@ struct fmt::formatter, Char> { #endif // FMT_API +#undef IS_SCALAR + #endif // LIBRAPID_AUTODIFF_DUAL \ No newline at end of file diff --git a/librapid/include/librapid/math/complex.hpp b/librapid/include/librapid/math/complex.hpp index 7c95777a..85f72fd4 100644 --- a/librapid/include/librapid/math/complex.hpp +++ b/librapid/include/librapid/math/complex.hpp @@ -20,10 +20,8 @@ # include #endif -#define REQUIRE_SCALAR(T) \ - typename std::enable_if_t<::librapid::typetraits::TypeInfo::type == \ - ::librapid::detail::LibRapidType::Scalar, \ - int> = 0 +#define IS_SCALAR(T) \ + ::librapid::typetraits::TypeInfo::type == ::librapid::detail::LibRapidType::Scalar namespace librapid { namespace detail { @@ -821,7 +819,8 @@ namespace librapid { /// \param left LHS complex number /// \param right RHS scalar /// \return Sum of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator+(const Complex &left, const R &right) { Complex tmp(left); @@ -838,7 +837,8 @@ namespace librapid { /// \param real LHS scalar /// \param right RHS complex number /// \return Sum of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator+(const R &real, const Complex &right) { Complex tmp(real); @@ -874,7 +874,8 @@ namespace librapid { /// \param left LHS complex number /// \param real RHS scalar /// \return Difference of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator-(const Complex &left, const R &real) { Complex tmp(left); @@ -892,7 +893,8 @@ namespace librapid { /// \param real LHS scalar /// \param right RHS complex number /// \return Difference of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator-(const R &real, const Complex &right) { Complex tmp(real); @@ -928,7 +930,8 @@ namespace librapid { /// \param left LHS complex number /// \param real RHS scalar /// \return Product of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator*(const Complex &left, const R &real) { Complex tmp(left); @@ -946,7 +949,8 @@ namespace librapid { /// \param real LHS scalar /// \param right RHS complex number /// \return Product of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator*(const R &real, const Complex &right) { Complex tmp(real); @@ -982,7 +986,8 @@ namespace librapid { /// \param left LHS complex number /// \param real RHS scalar /// \return Quotient of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator/(const Complex &left, const R &real) { Complex tmp(left); @@ -1000,7 +1005,8 @@ namespace librapid { /// \param real LHS scalar /// \param right RHS complex number /// \return Quotient of LHS and RHS - template + template + requires(IS_SCALAR(R)) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator/(const R &real, const Complex &right) { Complex tmp(real); @@ -1580,8 +1586,9 @@ namespace librapid { av = av * T(0.0625); bv = bv * T(0.0625); } else { - const T fltEps = typetraits::NumericInfo::epsilon(); - const T legTiny = fltEps == 0 ? T(0) : 2 * typetraits::NumericInfo::min() / fltEps; + const T fltEps = typetraits::NumericInfo::epsilon(); + const T legTiny = + fltEps == 0 ? T(0) : 2 * typetraits::NumericInfo::min() / fltEps; if (av < legTiny) { int64_t exponent; @@ -1718,9 +1725,8 @@ namespace librapid { /// \param left Complex base /// \param right Real exponent /// \return \f$ \text{left}^{\text{right}} \f$ - template::type == detail::LibRapidType::Scalar, int> = 0> + template + requires(typetraits::TypeInfo::type == detail::LibRapidType::Scalar) LIBRAPID_NODISCARD Complex pow(const Complex &left, const V &right) { if (imag(left) == 0) { if (::librapid::signBit(imag(left))) { @@ -1739,9 +1745,8 @@ namespace librapid { /// \param left Real base /// \param right Complex exponent /// \return \f$ \text{left}^{\text{right}} \f$ - template::type == detail::LibRapidType::Scalar, int> = 0> + template + requires(typetraits::TypeInfo::type == detail::LibRapidType::Scalar) LIBRAPID_NODISCARD Complex pow(const V &left, const Complex &right) { if (imag(right) == 0) { return _pow(static_cast(left), real(right)); @@ -2117,6 +2122,6 @@ struct fmt::formatter, Char> { # undef USE_ARM64_INTRINSICS #endif -#undef REQUIRE_SCALAR +#undef IS_SCALAR #endif // LIBRAPID_MATH_COMPLEX_HPP diff --git a/librapid/include/librapid/math/constants.hpp b/librapid/include/librapid/math/constants.hpp index 6d5f0604..d673284c 100644 --- a/librapid/include/librapid/math/constants.hpp +++ b/librapid/include/librapid/math/constants.hpp @@ -1,161 +1,161 @@ #ifndef LIBRAPID_MATH_CONSTANTS #define LIBRAPID_MATH_CONSTANTS -namespace librapid { -#define CTYPED static const double +namespace librapid::constants { +#define CTYPED constexpr double - /// 32bit float minimum value - CTYPED EPSILON32 = FLT_MIN; + /// 32bit float minimum value + CTYPED epsilon32 = FLT_MIN; - /// 64bit float minimum value - CTYPED EPSILON64 = DBL_MIN; + /// 64bit float minimum value + CTYPED epsilon64 = DBL_MIN; - /// PI squared on 6 - CTYPED PISQRDIV6 = 1.6449340668482264364724151666460251892189499012067984377355582293; + /// PI squared on 6 + CTYPED piSquaredOnSix = 1.6449340668482264364724151666460251892189499012067984377355582293; - /// 180/PI -- Radians to Degrees - CTYPED RADTODEG = 57.295779513082320876798154814105170332405472466564321549160243861; + /// 180/PI -- Radians to Degrees + CTYPED radToDeg = 57.295779513082320876798154814105170332405472466564321549160243861; - /// PI / 180 -- Degrees to Radians - CTYPED DEGTORAD = 0.0174532925199432957692369076848861271344287188854172545609719144; + /// PI / 180 -- Degrees to Radians + CTYPED degToRad = 0.0174532925199432957692369076848861271344287188854172545609719144; - /// PI - CTYPED PI = 3.1415926535897932384626433832795028841971693993751058209749445923; + /// PI + CTYPED pi = 3.1415926535897932384626433832795028841971693993751058209749445923; - /// Sqrt(PI) - CTYPED SQRTPI = 1.7724538509055160272981674833411451827975494561223871282138077898; + /// Sqrt(PI) + CTYPED sqrtPi = 1.7724538509055160272981674833411451827975494561223871282138077898; - /// Tau - CTYPED TAU = 6.2831853071795864769252867665590057683943387987502116419498891846; + /// Tau + CTYPED tau = 6.2831853071795864769252867665590057683943387987502116419498891846; - /// PI/2 - CTYPED HALFPI = 1.5707963267948966192313216916397514420985846996875529104874722961; + /// PI/2 + CTYPED halfPi = 1.5707963267948966192313216916397514420985846996875529104874722961; - /// 2*PI - CTYPED TWOPI = 6.2831853071795864769252867665590057683943387987502116419498891846156; + /// 2*PI + CTYPED twoPi = 6.2831853071795864769252867665590057683943387987502116419498891846156; - /// E - CTYPED EULER = 2.7182818284590452353602874713526624977572470936999595749669676277; + /// E + CTYPED e = 2.7182818284590452353602874713526624977572470936999595749669676277; - /// Sqrt(E) - CTYPED SQRTE = 1.6487212707001281468486507878141635716537761007101480115750793116; + /// Sqrt(E) + CTYPED sqrtE = 1.6487212707001281468486507878141635716537761007101480115750793116; - /// Sqrt(2) - CTYPED SQRT2 = 1.4142135623730950488016887242096980785696718753769480731766797379; + /// Sqrt(2) + CTYPED sqrt2 = 1.4142135623730950488016887242096980785696718753769480731766797379; - /// Sqrt(3) - CTYPED SQRT3 = 1.7320508075688772935274463415058723669428052538103806280558069794; + /// Sqrt(3) + CTYPED sqrt3 = 1.7320508075688772935274463415058723669428052538103806280558069794; - /// Sqrt(5) - CTYPED SQRT5 = 2.2360679774997896964091736687312762354406183596115257242708972454; + /// Sqrt(5) + CTYPED sqrt5 = 2.2360679774997896964091736687312762354406183596115257242708972454; - /// Golden Ratio - CTYPED GOLDENRATIO = 1.6180339887498948482045868343656381177203091798057628621354486227; + /// Golden Ratio + CTYPED goldenRatio = 1.6180339887498948482045868343656381177203091798057628621354486227; - /// Euler-Mascheroni constant - CTYPED EULERMASCHERONI = 0.5772156649015328606065120900824024310421593359399235988057672348; + /// Euler-Mascheroni constant + CTYPED eulerMascheroni = 0.5772156649015328606065120900824024310421593359399235988057672348; - /// Twin Primes Constant - CTYPED TWINPRIMES = 0.6601618158468695739278121100145557784326233602847334133194484233; + /// Twin Primes Constant + CTYPED twinPrimes = 0.6601618158468695739278121100145557784326233602847334133194484233; - /// Ln(2) - CTYPED LN2 = 0.6931471805599453094172321214581765680755001343602552541206800094; + /// Ln(2) + CTYPED ln2 = 0.6931471805599453094172321214581765680755001343602552541206800094; - /// Ln(3) - CTYPED LN3 = 1.0986122886681096913952452369225257046474905578227494517346943336; + /// Ln(3) + CTYPED ln3 = 1.0986122886681096913952452369225257046474905578227494517346943336; - /// Ln(5) - CTYPED LN5 = 1.6094379124341003746007593332261876395256013542685177219126478914; + /// Ln(5) + CTYPED ln5 = 1.6094379124341003746007593332261876395256013542685177219126478914; - /// Zeta(3) - CTYPED ZETA3 = 1.2020569031595942853997381615114499907649862923404988817922715553; + /// Zeta(3) + CTYPED zeta3 = 1.2020569031595942853997381615114499907649862923404988817922715553; - /// CubeRoot(2) - CTYPED CUBEROOT2 = 1.2599210498948731647672106072782283505702514647015079800819751121; + /// CubeRoot(2) + CTYPED cbrt2 = 1.2599210498948731647672106072782283505702514647015079800819751121; - /// CubeRoot(3) - CTYPED CUBEROOT3 = 1.4422495703074083823216383107801095883918692534993505775464161945; + /// CubeRoot(3) + CTYPED cbrt3 = 1.4422495703074083823216383107801095883918692534993505775464161945; - /// Speed of Light - CTYPED LIGHTSPEED = 299792458.0; + /// Speed of Light + CTYPED lightSpeed = 299792458.0; - /// Acceleration due to gravity on Earth - CTYPED EARTHGRAVITY = 9.80665; + /// Acceleration due to gravity on Earth + CTYPED earthGravity = 9.80665; - /// Wallis Constant - CTYPED WALLISCONST = 2.0945514815423265914823865405793029638573061056282391803041285290; + /// Wallis Constant + CTYPED wallisConstant = 2.0945514815423265914823865405793029638573061056282391803041285290; - /// Laplace limit - CTYPED LAPLACELIMIT = 0.6627434193491815809747420971092529070562335491150224175203925349; + /// Laplace limit + CTYPED laplaceLimit = 0.6627434193491815809747420971092529070562335491150224175203925349; - /// Gauss's constant - CTYPED GAUSSCONST = 0.8346268416740731862814297327990468089939930134903470024498273701; + /// Gauss's constant + CTYPED gaussConstant = 0.8346268416740731862814297327990468089939930134903470024498273701; - /// Cahen's constant - CTYPED CAHENSCONST = 0.6434105462883380261822543077575647632865878602682395059870309203; + /// Cahen's constant + CTYPED cahenConstant = 0.6434105462883380261822543077575647632865878602682395059870309203; - /// Parabolic constant -- P_2 - CTYPED P2 = 2.2955871493926380740342980491894903875978322036385834839299753466; + /// Parabolic constant -- P_2 + CTYPED parabolic2 = 2.2955871493926380740342980491894903875978322036385834839299753466; - /// Dottie number - CTYPED DOTTIENUMBER = 0.7390851332151606416553120876738734040134117589007574649656806357; + /// Dottie number + CTYPED dottieNumber = 0.7390851332151606416553120876738734040134117589007574649656806357; - /// Meissel-Mertens constant - CTYPED MEISSELMERTENS = 0.2614972128476427837554268386086958590515666482611992061920642139; + /// Meissel-Mertens constant + CTYPED meisselMertens = 0.2614972128476427837554268386086958590515666482611992061920642139; - /// E^PI -- Gelfond's constant - CTYPED ETOPI = 23.140692632779269005729086367948547380266106242600211993445046409; + /// E^PI -- Gelfond's constant + CTYPED eToPi = 23.140692632779269005729086367948547380266106242600211993445046409; - /// Golden angle - CTYPED GOLDENANGLE = 2.3999632297286533222315555066336138531249990110581150429351127507; + /// Golden angle + CTYPED goldenAngle = 2.3999632297286533222315555066336138531249990110581150429351127507; - /// Area of the Mandelbrot fractal. - CTYPED MANDELBROTAREA = 1.5065918849; + /// Area of the Mandelbrot fractal. + CTYPED mandelbrotArea = 1.5065918849; - /// Gieseking constant. - CTYPED GIESEKINGCONST = 1.0149416064096536250212025542745202859416893075302997920174891067; + /// Gieseking constant. + CTYPED gieseking = 1.0149416064096536250212025542745202859416893075302997920174891067; - /// Bloch-Landau constant. - CTYPED BLOCHLANDAU = 0.5432589653429767069527282953006132311388632937583569889557325691; + /// Bloch-Landau constant. + CTYPED blochLandau = 0.5432589653429767069527282953006132311388632937583569889557325691; - /// Golomb-Dickman constant. - CTYPED GOLOMBDICKMAN = 0.6243299885435508709929363831008372441796426201805292869735519024; + /// Golomb-Dickman constant. + CTYPED golombDickman = 0.6243299885435508709929363831008372441796426201805292869735519024; - /// Feller-Tornier constant. - CTYPED FELLERTORNIER = 0.6613170494696223352897658462741185332854752898329; + /// Feller-Tornier constant. + CTYPED fellerTornier = 0.6613170494696223352897658462741185332854752898329; - /// 2^Sqrt(2) - CTYPED TWOTOSQRT2 = 2.6651441426902251886502972498731398482742113137146594928359795933; + /// 2^Sqrt(2) + CTYPED twoToRoot2 = 2.6651441426902251886502972498731398482742113137146594928359795933; - /// Khinchin's constant - CTYPED KHINCHINSCONST = 2.6854520010653064453097148354817956938203822939944629530511523455; + /// Khinchin's constant + CTYPED khinchin = 2.6854520010653064453097148354817956938203822939944629530511523455; - /// Mill's constant - CTYPED MILLSCONST = 1.3063778838630806904686144926026057129167845851567136443680537599; + /// Mill's constant + CTYPED mill = 1.3063778838630806904686144926026057129167845851567136443680537599; - /// PI/Ln(2) - CTYPED PIOVERLN2 = 4.5323601418271938096276829457166668101718614677237955841860165479; + /// PI/Ln(2) + CTYPED piOnLn2 = 4.5323601418271938096276829457166668101718614677237955841860165479; - /// Loch's constant - CTYPED LOCHSCONST = 0.9702701143920339257402560192100108337812847047851612866103505299; + /// Loch's constant + CTYPED lochConstant = 0.9702701143920339257402560192100108337812847047851612866103505299; - /// Niven's constant - CTYPED NIVENSCONST = 1.7052111401053677642885514534345081607620276516534690999942849065; + /// Niven's constant + CTYPED nivenConstant = 1.7052111401053677642885514534345081607620276516534690999942849065; - /// Reciprocal Fibonacci constant - CTYPED RECIPFIBCONST = 3.3598856662431775531720113029189271796889051337319684864955538153; + /// Reciprocal Fibonacci constant + CTYPED recipFib = 3.3598856662431775531720113029189271796889051337319684864955538153; - /// Backhouse's constant - CTYPED BACKHOUSECONST = 1.4560749485826896713995953511165435576531783748471315402707024374; + /// Backhouse's constant + CTYPED backhouse = 1.4560749485826896713995953511165435576531783748471315402707024374; - /// MRB constant - CTYPED MRBCONST = 0.1878596424620671202485179340542732300559030949001387861720046840; + /// MRB constant + CTYPED mbr = 0.1878596424620671202485179340542732300559030949001387861720046840; - /// Somos' quadratic recurrence constant - CTYPED QUADRECURR = 1.6616879496335941212958189227499507499644186350250682081897111680; + /// Somos' quadratic recurrence constant + CTYPED quadRecurrence = 1.6616879496335941212958189227499507499644186350250682081897111680; - /// Plastic number - CTYPED PLASTICNUMBER = 1.3247179572447460259609088544780973407344040569017333645340150503; -} // namespace librapid + /// Plastic number + CTYPED plasticNumber = 1.3247179572447460259609088544780973407344040569017333645340150503; +} // namespace librapid::constants #endif // LIBRAPID_MATH_CONSTANTS diff --git a/librapid/include/librapid/math/coreMath.hpp b/librapid/include/librapid/math/coreMath.hpp index 91ac1e2d..a703d680 100644 --- a/librapid/include/librapid/math/coreMath.hpp +++ b/librapid/include/librapid/math/coreMath.hpp @@ -9,450 +9,472 @@ */ namespace librapid { - namespace detail { - template - struct ContainsArrayType; - } // namespace detail - - /// Return the smallest value of a given set of values - /// \tparam T Data type - /// \param val Input set - /// \return Smallest element of the input set - template - T &&min(T &&val) { - return std::forward(val); - } - - /// Return the smallest value of a given set of values - /// \tparam Types Data types of the input values - /// \param vals Input values - /// \return The smallest element of the input values - template - auto min(T0 &&val1, T1 &&val2, Ts &&...vs) { - return (val1 < val2) ? min(val1, std::forward(vs)...) - : min(val2, std::forward(vs)...); - } - - /// Return the largest value of a given set of values - /// \tparam T Data type - /// \param val Input set - /// \return Largest element of the input set - template - T &&max(T &&val) { - return std::forward(val); - } - - /// Return the largest value of a given set of values - /// \tparam Types Data types of the input values - /// \param vals Input values - /// \return The largest element of the input values - template - auto max(T0 &&val1, T1 &&val2, Ts &&...vs) { - return (val1 > val2) ? max(val1, std::forward(vs)...) - : max(val2, std::forward(vs)...); - } - - /// Return the absolute value of a given value - /// \tparam T Data type - /// \param val Input value - /// \return Absolute value of the input value - template, int> = 0> - constexpr T abs(T val) { - return (val < T(0)) ? -val : val; - } - - /// Map a value from one range to another - /// \tparam V Data type of the value to map - /// \tparam B1 Data type of the lower bound of the input range - /// \tparam E1 Data type of the upper bound of the input range - /// \tparam B2 Data type of the lower bound of the output range - /// \tparam E2 Data type of the upper bound of the output range - /// \param val Value to map - /// \param start1 Lower bound of the input range - /// \param stop1 Upper bound of the input range - /// \param start2 Lower bound of the output range - /// \param stop2 Upper bound of the output range - /// \return Mapped value - template - LIBRAPID_INLINE auto map(const V &val, const B1 &start1, const E1 &stop1, const B2 &start2, - const E2 &stop2) { - return start2 + (val - start1) * (stop2 - start2) / (stop1 - start1); - - // if constexpr (detail::ContainsArrayType::val) { - // return start2 + (val - start1) * (stop2 - start2) / (stop1 - start1); - // } else { - // using T = decltype((val - start1) * (stop2 - start2) / (stop1 - start1) + start2); - // return static_cast(start2) + (static_cast(stop2) - static_cast(start2)) * - // ((static_cast(val) - static_cast(start1)) / - // (static_cast(stop1) - static_cast(start1))); - // } - } - - template - LIBRAPID_INLINE auto mod(const T1 &val, const T2 &mod) { - if constexpr (std::is_floating_point_v || std::is_floating_point_v) { - return std::fmod(val, mod); - } else { - return val % mod; - } - } - - /// Return the floor of a given value - /// \tparam T Data type - /// \param val Input value - /// \return Floor of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr T floor(T val) { - return std::floor(val); - } - - /// Return the ceiling of a given value - /// \tparam T Data type - /// \param val Input value - /// \return Ceiling of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr T ceil(T val) { - return std::ceil(val); - } - - /// Return the square root of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the square root. - /// \tparam T Data type - /// \param val Input value - /// \return Square root of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto sqrt(T val) { - if constexpr (std::is_integral_v) { - return std::sqrt(static_cast(val)); - } else { - return std::sqrt(val); - } - } - - /// Return the hypotenuse of a right triangle given the lengths of the two legs. Note that, - /// for integer values, this function will cast the input values to a floating point type - /// before calculating the hypotenuse. - /// \tparam T Data type - /// \param leg1 Length of the first leg - /// \param leg2 Length of the second leg - /// \return Hypotenuse of the right triangle - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto hypot(T leg1, T leg2) { - if constexpr (std::is_integral_v) { - return std::hypot(static_cast(leg1), static_cast(leg2)); - } else { - return std::hypot(leg1, leg2); - } - } - - /// Return the cube root of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the cube root. - /// \tparam T Data type - /// \param val Input value - /// \return Cube root of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto cbrt(T val) { - if constexpr (std::is_integral_v) { - return std::cbrt(static_cast(val)); - } else { - return std::cbrt(val); - } - } - - /// Return the first number raised to the power of the second number. The return value will be - /// promoted to the larger of the two input types. - /// \tparam T0 Data type of the first input value - /// \tparam T1 Data type of the second input value - /// \param val1 First input value - /// \param val2 Second input value - /// \return First input value raised to the power of the second input value - template< - typename T0, typename T1, - typename std::enable_if_t && std::is_fundamental_v, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto pow(T0 val1, T1 val2) { - if constexpr (std::is_integral_v && std::is_integral_v) { - return std::pow(static_cast(val1), static_cast(val2)); - } else if constexpr (std::is_integral_v) { - return std::pow(static_cast(val1), val2); - } else if constexpr (std::is_integral_v) { - return std::pow(val1, static_cast(val2)); - } else { - return std::pow(val1, val2); - } - } - - /// Return the exponential of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the exponential. - /// \tparam T Data type - /// \param val Input value - /// \return Exponential of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto exp(T val) { - if constexpr (std::is_integral_v) { - return std::exp(static_cast(val)); - } else { - return std::exp(val); - } - } - - /// Return 2 raised to a given power. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the exponential. - /// \tparam T Data type - /// \param val Input value - /// \return 2 raised to the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto exp2(T val) { - if constexpr (std::is_integral_v) { - return std::exp2(static_cast(val)); - } else { - return std::exp2(val); - } - } - - // Return 10 raised to a given power. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the exponential. - /// \tparam T Data type - /// \param val Input value - /// \return 10 raised to the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto exp10(T val) { - // C++ standard does not implement exp10 - - if constexpr (std::is_integral_v) { - return std::pow(10.0, static_cast(val)); - } else { - return std::pow(10.0, val); - } - } - - /// Return the natural logarithm of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the logarithm. - /// \tparam T Data type - /// \param val Input value - /// \return Natural logarithm of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto log(T val) { - if constexpr (std::is_integral_v) { - return std::log(static_cast(val)); - } else { - return std::log(val); - } - } - - /// Return the logarithm base-10 of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the logarithm. - /// \tparam T Data type - /// \param val Input value - /// \return Logarithm base-10 of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto log10(T val) { - if constexpr (std::is_integral_v) { - return std::log10(static_cast(val)); - } else { - return std::log10(val); - } - } - - /// Return the logarithm base-2 of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the logarithm. - /// \tparam T Data type - /// \param val Input value - /// \return Logarithm base-2 of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto log2(T val) { - if constexpr (std::is_integral_v) { - return std::log2(static_cast(val)); - } else { - return std::log2(val); - } - } - - /// Return the sine of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the sine. - /// \tparam T Data type - /// \param val Input value - /// \return Sine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto sin(T val) { - if constexpr (std::is_integral_v) { - return std::sin(static_cast(val)); - } else { - return std::sin(val); - } - } - - /// Return the cosine of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the cosine. - /// \tparam T Data type - /// \param val Input value - /// \return Cosine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto cos(T val) { - if constexpr (std::is_integral_v) { - return std::cos(static_cast(val)); - } else { - return std::cos(val); - } - } - - /// Return the tangent of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the tangent. - /// \tparam T Data type - /// \param val Input value - /// \return Tangent of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto tan(T val) { - if constexpr (std::is_integral_v) { - return std::tan(static_cast(val)); - } else { - return std::tan(val); - } - } - - /// Return the arcsine of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the arcsine. - /// \tparam T Data type - /// \param val Input value - /// \return Arcsine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto asin(T val) { - if constexpr (std::is_integral_v) { - return std::asin(static_cast(val)); - } else { - return std::asin(val); - } - } - - /// Return the arccosine of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the arccosine. - /// \tparam T Data type - /// \param val Input value - /// \return Arccosine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto acos(T val) { - if constexpr (std::is_integral_v) { - return std::acos(static_cast(val)); - } else { - return std::acos(val); - } - } - - /// Return the arctangent of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before calculating the arctangent. - /// \tparam T Data type - /// \param val Input value - /// \return Arctangent of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto atan(T val) { - if constexpr (std::is_integral_v) { - return std::atan(static_cast(val)); - } else { - return std::atan(val); - } - } - - /// Return the angle formed by a given y and x offset. This is often more useful than using - /// atan, since it gives more usable outputs. Note that, for integer values, this function - /// will cast the input values to a floating point type before calculating the angle. - /// \tparam TY Data type of the y offset - /// \tparam TX Data type of the x offset - /// \param dy Y offset - /// \param dx X offset - /// \return Angle formed by the given offsets - template< - typename TY, typename TX, - typename std::enable_if_t && std::is_fundamental_v, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto atan2(TY dy, TX dx) { - if constexpr (std::is_integral_v || std::is_integral_v) { - return std::atan2(static_cast(dy), static_cast(dx)); - } else { - return std::atan2(dy, dx); - } - } - - /// Return the hyperbolic sin of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before computing the result. - /// \tparam T Data type - /// \param val Input value - /// \return Hyperbolic sine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto sinh(T val) { - if constexpr (std::is_integral_v) { - return std::sinh(static_cast(val)); - } else { - return std::sinh(val); - } - } - - /// Return the hyperbolic cosine of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before computing the result. - /// \tparam T Data type - /// \param val Input value - /// \return Hyperbolic cosine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto cosh(T val) { - if constexpr (std::is_integral_v) { - return std::cosh(static_cast(val)); - } else { - return std::cosh(val); - } - } - - /// Return the hyperbolic tangent of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before computing the result. - /// \tparam T Data type - /// \param val Input value - /// \return Hyperbolic tangent of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto tanh(T val) { - if constexpr (std::is_integral_v) { - return std::tanh(static_cast(val)); - } else { - return std::tanh(val); - } - } - - /// Return the hyperbolic arcsine of a given value. Note that, for integer values, this function - /// will cast the input value to a floating point type before computing the result. - /// \tparam T Data type - /// \param val Input value - /// \return Hyperbolic arcsine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto asinh(T val) { - if constexpr (std::is_integral_v) { - return std::asinh(static_cast(val)); - } else { - return std::asinh(val); - } - } - - /// Return the hyperbolic arccosine of a given value. Note that, for integer values, this - /// function will cast the input value to a floating point type before computing the result. - /// \tparam T Data type - /// \param val Input value - /// \return Hyperbolic arccosine of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto acosh(T val) { - if constexpr (std::is_integral_v) { - return std::acosh(static_cast(val)); - } else { - return std::acosh(val); - } - } - - /// Return the hyperbolic arctangent of a given value. Note that, for integer values, this - /// function will cast the input value to a floating point type before computing the result. - /// \tparam T Data type - /// \param val Input value - /// \return Hyperbolic arctangent of the input value - template, int> = 0> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto atanh(T val) { - if constexpr (std::is_integral_v) { - return std::atanh(static_cast(val)); - } else { - return std::atanh(val); - } - } + namespace detail { + template + struct ContainsArrayType; + } // namespace detail + + /// Return the smallest value of a given set of values + /// \tparam T Data type + /// \param val Input set + /// \return Smallest element of the input set + template + T &&min(T &&val) { + return std::forward(val); + } + + /// Return the smallest value of a given set of values + /// \tparam Types Data types of the input values + /// \param vals Input values + /// \return The smallest element of the input values + template + auto min(T0 &&val1, T1 &&val2, Ts &&...vs) { + return (val1 < val2) ? min(val1, std::forward(vs)...) + : min(val2, std::forward(vs)...); + } + + /// Return the largest value of a given set of values + /// \tparam T Data type + /// \param val Input set + /// \return Largest element of the input set + template + T &&max(T &&val) { + return std::forward(val); + } + + /// Return the largest value of a given set of values + /// \tparam Types Data types of the input values + /// \param vals Input values + /// \return The largest element of the input values + template + auto max(T0 &&val1, T1 &&val2, Ts &&...vs) { + return (val1 > val2) ? max(val1, std::forward(vs)...) + : max(val2, std::forward(vs)...); + } + + /// Return the absolute value of a given value + /// \tparam T Data type + /// \param val Input value + /// \return Absolute value of the input value + template + requires(std::is_fundamental_v) + constexpr T abs(T val) { + return (val < T(0)) ? -val : val; + } + + /// Map a value from one range to another + /// \tparam V Data type of the value to map + /// \tparam B1 Data type of the lower bound of the input range + /// \tparam E1 Data type of the upper bound of the input range + /// \tparam B2 Data type of the lower bound of the output range + /// \tparam E2 Data type of the upper bound of the output range + /// \param val Value to map + /// \param start1 Lower bound of the input range + /// \param stop1 Upper bound of the input range + /// \param start2 Lower bound of the output range + /// \param stop2 Upper bound of the output range + /// \return Mapped value + template + LIBRAPID_INLINE auto map(const V &val, const B1 &start1, const E1 &stop1, const B2 &start2, + const E2 &stop2) { + return start2 + (val - start1) * (stop2 - start2) / (stop1 - start1); + + // if constexpr (detail::ContainsArrayType::val) { + // return start2 + (val - start1) * (stop2 - start2) / (stop1 - start1); + // } else { + // using T = decltype((val - start1) * (stop2 - start2) / (stop1 - start1) + start2); + // return static_cast(start2) + (static_cast(stop2) - static_cast(start2)) * + // ((static_cast(val) - static_cast(start1)) / + // (static_cast(stop1) - static_cast(start1))); + // } + } + + template + LIBRAPID_INLINE auto mod(const T1 &val, const T2 &mod) { + if constexpr (std::is_floating_point_v || std::is_floating_point_v) { + return std::fmod(val, mod); + } else { + return val % mod; + } + } + + /// Return the floor of a given value + /// \tparam T Data type + /// \param val Input value + /// \return Floor of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr T floor(T val) { + return std::floor(val); + } + + /// Return the ceiling of a given value + /// \tparam T Data type + /// \param val Input value + /// \return Ceiling of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr T ceil(T val) { + return std::ceil(val); + } + + /// Return the square root of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the square root. + /// \tparam T Data type + /// \param val Input value + /// \return Square root of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto sqrt(T val) { + if constexpr (std::is_integral_v) { + return std::sqrt(static_cast(val)); + } else { + return std::sqrt(val); + } + } + + /// Return the hypotenuse of a right triangle given the lengths of the two legs. Note that, + /// for integer values, this function will cast the input values to a floating point type + /// before calculating the hypotenuse. + /// \tparam T Data type + /// \param leg1 Length of the first leg + /// \param leg2 Length of the second leg + /// \return Hypotenuse of the right triangle + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto hypot(T leg1, T leg2) { + if constexpr (std::is_integral_v) { + return std::hypot(static_cast(leg1), static_cast(leg2)); + } else { + return std::hypot(leg1, leg2); + } + } + + /// Return the cube root of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the cube root. + /// \tparam T Data type + /// \param val Input value + /// \return Cube root of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto cbrt(T val) { + if constexpr (std::is_integral_v) { + return std::cbrt(static_cast(val)); + } else { + return std::cbrt(val); + } + } + + /// Return the first number raised to the power of the second number. The return value will be + /// promoted to the larger of the two input types. + /// \tparam T0 Data type of the first input value + /// \tparam T1 Data type of the second input value + /// \param val1 First input value + /// \param val2 Second input value + /// \return First input value raised to the power of the second input value + template + requires(std::is_fundamental_v && std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto pow(T0 val1, T1 val2) { + if constexpr (std::is_integral_v && std::is_integral_v) { + return std::pow(static_cast(val1), static_cast(val2)); + } else if constexpr (std::is_integral_v) { + return std::pow(static_cast(val1), val2); + } else if constexpr (std::is_integral_v) { + return std::pow(val1, static_cast(val2)); + } else { + return std::pow(val1, val2); + } + } + + /// Return the exponential of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the exponential. + /// \tparam T Data type + /// \param val Input value + /// \return Exponential of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto exp(T val) { + if constexpr (std::is_integral_v) { + return std::exp(static_cast(val)); + } else { + return std::exp(val); + } + } + + /// Return 2 raised to a given power. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the exponential. + /// \tparam T Data type + /// \param val Input value + /// \return 2 raised to the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto exp2(T val) { + if constexpr (std::is_integral_v) { + return std::exp2(static_cast(val)); + } else { + return std::exp2(val); + } + } + + // Return 10 raised to a given power. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the exponential. + /// \tparam T Data type + /// \param val Input value + /// \return 10 raised to the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto exp10(T val) { + // C++ standard does not implement exp10 + + if constexpr (std::is_integral_v) { + return std::pow(10.0, static_cast(val)); + } else { + return std::pow(10.0, val); + } + } + + /// Return the natural logarithm of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the logarithm. + /// \tparam T Data type + /// \param val Input value + /// \return Natural logarithm of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto log(T val) { + if constexpr (std::is_integral_v) { + return std::log(static_cast(val)); + } else { + return std::log(val); + } + } + + /// Return the logarithm base-10 of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the logarithm. + /// \tparam T Data type + /// \param val Input value + /// \return Logarithm base-10 of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto log10(T val) { + if constexpr (std::is_integral_v) { + return std::log10(static_cast(val)); + } else { + return std::log10(val); + } + } + + /// Return the logarithm base-2 of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the logarithm. + /// \tparam T Data type + /// \param val Input value + /// \return Logarithm base-2 of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto log2(T val) { + if constexpr (std::is_integral_v) { + return std::log2(static_cast(val)); + } else { + return std::log2(val); + } + } + + /// Return the sine of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the sine. + /// \tparam T Data type + /// \param val Input value + /// \return Sine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto sin(T val) { + if constexpr (std::is_integral_v) { + return std::sin(static_cast(val)); + } else { + return std::sin(val); + } + } + + /// Return the cosine of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the cosine. + /// \tparam T Data type + /// \param val Input value + /// \return Cosine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto cos(T val) { + if constexpr (std::is_integral_v) { + return std::cos(static_cast(val)); + } else { + return std::cos(val); + } + } + + /// Return the tangent of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the tangent. + /// \tparam T Data type + /// \param val Input value + /// \return Tangent of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto tan(T val) { + if constexpr (std::is_integral_v) { + return std::tan(static_cast(val)); + } else { + return std::tan(val); + } + } + + /// Return the arcsine of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the arcsine. + /// \tparam T Data type + /// \param val Input value + /// \return Arcsine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto asin(T val) { + if constexpr (std::is_integral_v) { + return std::asin(static_cast(val)); + } else { + return std::asin(val); + } + } + + /// Return the arccosine of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the arccosine. + /// \tparam T Data type + /// \param val Input value + /// \return Arccosine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto acos(T val) { + if constexpr (std::is_integral_v) { + return std::acos(static_cast(val)); + } else { + return std::acos(val); + } + } + + /// Return the arctangent of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before calculating the arctangent. + /// \tparam T Data type + /// \param val Input value + /// \return Arctangent of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto atan(T val) { + if constexpr (std::is_integral_v) { + return std::atan(static_cast(val)); + } else { + return std::atan(val); + } + } + + /// Return the angle formed by a given y and x offset. This is often more useful than using + /// atan, since it gives more usable outputs. Note that, for integer values, this function + /// will cast the input values to a floating point type before calculating the angle. + /// \tparam TY Data type of the y offset + /// \tparam TX Data type of the x offset + /// \param dy Y offset + /// \param dx X offset + /// \return Angle formed by the given offsets + template + requires(std::is_fundamental_v && std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto atan2(TY dy, TX dx) { + if constexpr (std::is_integral_v || std::is_integral_v) { + return std::atan2(static_cast(dy), static_cast(dx)); + } else { + return std::atan2(dy, dx); + } + } + + /// Return the hyperbolic sin of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before computing the result. + /// \tparam T Data type + /// \param val Input value + /// \return Hyperbolic sine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto sinh(T val) { + if constexpr (std::is_integral_v) { + return std::sinh(static_cast(val)); + } else { + return std::sinh(val); + } + } + + /// Return the hyperbolic cosine of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before computing the result. + /// \tparam T Data type + /// \param val Input value + /// \return Hyperbolic cosine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto cosh(T val) { + if constexpr (std::is_integral_v) { + return std::cosh(static_cast(val)); + } else { + return std::cosh(val); + } + } + + /// Return the hyperbolic tangent of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before computing the result. + /// \tparam T Data type + /// \param val Input value + /// \return Hyperbolic tangent of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto tanh(T val) { + if constexpr (std::is_integral_v) { + return std::tanh(static_cast(val)); + } else { + return std::tanh(val); + } + } + + /// Return the hyperbolic arcsine of a given value. Note that, for integer values, this function + /// will cast the input value to a floating point type before computing the result. + /// \tparam T Data type + /// \param val Input value + /// \return Hyperbolic arcsine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto asinh(T val) { + if constexpr (std::is_integral_v) { + return std::asinh(static_cast(val)); + } else { + return std::asinh(val); + } + } + + /// Return the hyperbolic arccosine of a given value. Note that, for integer values, this + /// function will cast the input value to a floating point type before computing the result. + /// \tparam T Data type + /// \param val Input value + /// \return Hyperbolic arccosine of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto acosh(T val) { + if constexpr (std::is_integral_v) { + return std::acosh(static_cast(val)); + } else { + return std::acosh(val); + } + } + + /// Return the hyperbolic arctangent of a given value. Note that, for integer values, this + /// function will cast the input value to a floating point type before computing the result. + /// \tparam T Data type + /// \param val Input value + /// \return Hyperbolic arctangent of the input value + template + requires(std::is_fundamental_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE constexpr auto atanh(T val) { + if constexpr (std::is_integral_v) { + return std::atanh(static_cast(val)); + } else { + return std::atanh(val); + } + } } // namespace librapid #endif // LIBRAPID_MATH_CORE_MATH_HPP diff --git a/librapid/include/librapid/math/utilityFunctions.hpp b/librapid/include/librapid/math/utilityFunctions.hpp index b0fd2afc..8ec90503 100644 --- a/librapid/include/librapid/math/utilityFunctions.hpp +++ b/librapid/include/librapid/math/utilityFunctions.hpp @@ -2,157 +2,149 @@ #define LIBRAPID_MATH_UTLIITY_FUNCTIONS_HPP namespace librapid { - /// \brief Limit a value to a specified range - /// - /// \f$ C(x, m, M) = \left\{ \begin{align*} x & \quad m \le x \le M \\ m & \quad x < m \\ M & - /// \quad x > M \end{align*}\right. \f$ - /// - /// If M < m, the values are swapped to make the function valid. - /// For example, `clamp(5, 10, 0)` still returns `5`. - /// - /// \tparam X Type of \p x - /// \tparam Lower Type of \p lowerLimit - /// \tparam Upper Type of \p upperLimit - /// \param x Value to limit - /// \param lowerLimit Lower bound (m) - /// \param upperLimit Upper bound (M) - /// \return \p x limited to the range [\p lowerLimit, \p upperLimit] - template::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar, - int> = 0, - typename ST = typetraits::ScalarReturnType> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST clamp(X x, Lower lowerLimit, Upper upperLimit) { - LIBRAPID_ASSERT(lowerLimit < upperLimit, "Lower limit must be below upper limit"); - if (x < lowerLimit) return static_cast(lowerLimit); - if (x > upperLimit) return static_cast(upperLimit); - return x; - } + /// \brief Limit a value to a specified range + /// + /// \f$ C(x, m, M) = \left\{ \begin{align*} x & \quad m \le x \le M \\ m & \quad x < m \\ M & + /// \quad x > M \end{align*}\right. \f$ + /// + /// If M < m, the values are swapped to make the function valid. + /// For example, `clamp(5, 10, 0)` still returns `5`. + /// + /// \tparam X Type of \p x + /// \tparam Lower Type of \p lowerLimit + /// \tparam Upper Type of \p upperLimit + /// \param x Value to limit + /// \param lowerLimit Lower bound (m) + /// \param upperLimit Upper bound (M) + /// \return \p x limited to the range [\p lowerLimit, \p upperLimit] + template> + requires(typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST clamp(X x, Lower lowerLimit, Upper upperLimit) { + LIBRAPID_ASSERT(lowerLimit < upperLimit, "Lower limit must be below upper limit"); + if (x < lowerLimit) return static_cast(lowerLimit); + if (x > upperLimit) return static_cast(upperLimit); + return x; + } - /// \brief Linearly interpolate between two values - /// - /// \f$ \mathrm{lerp}(t, L, U) = L+t\left( U-L \right) \f$ - /// - /// \tparam T Type of \p t - /// \tparam Lower Type of \p lower - /// \tparam Upper Type of \p upper - /// \param t Interpolation Percentage - /// \param lower Lower bound (L) - /// \param upper Upper bound (U) - /// \return - template::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar && - std::is_floating_point_v && std::is_floating_point_v && - std::is_floating_point_v, - int> = 0, - typename ST = typetraits::ScalarReturnType> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST lerp(T t, Lower lower, Upper upper) { - if (isNaN(t) || isNaN(lower) || isNaN(upper)) - return std::numeric_limits::quiet_NaN(); - else if ((t <= ST {0} && upper >= Upper {0}) || (lower >= Lower {0} && upper <= Upper {0})) - // ab <= 0 but product could overflow. + /// \brief Linearly interpolate between two values + /// + /// \f$ \mathrm{lerp}(t, L, U) = L+t\left( U-L \right) \f$ + /// + /// \tparam T Type of \p t + /// \tparam Lower Type of \p lower + /// \tparam Upper Type of \p upper + /// \param t Interpolation Percentage + /// \param lower Lower bound (L) + /// \param upper Upper bound (U) + /// \return + template> + requires(typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + std::is_floating_point_v && std::is_floating_point_v && + std::is_floating_point_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST lerp(T t, Lower lower, Upper upper) { + if (isNaN(t) || isNaN(lower) || isNaN(upper)) + return std::numeric_limits::quiet_NaN(); + else if ((t <= ST {0} && upper >= Upper {0}) || (lower >= Lower {0} && upper <= Upper {0})) + // ab <= 0 but product could overflow. #ifndef FMA - return t * upper + (ST {1} - t) * lower; + return t * upper + (ST {1} - t) * lower; #else - return std::fma(t, upper, (_Float {1} - t) * upper); + return std::fma(t, upper, (_Float {1} - t) * upper); #endif - else if (t == ST {1}) - return upper; - else { // monotonic near t == 1. + else if (t == ST {1}) + return upper; + else { // monotonic near t == 1. #ifndef FMA - const auto x = lower + t * (upper - lower); + const auto x = lower + t * (upper - lower); #else - const auto x = std::fma(t, upper - lower, lower); + const auto x = std::fma(t, upper - lower, lower); #endif - return (t > ST {1}) == (upper > lower) ? max(upper, x) : min(upper, x); - } - } + return (t > ST {1}) == (upper > lower) ? max(upper, x) : min(upper, x); + } + } - /// \brief Linearly interpolate between two values - /// - /// \f$ \mathrm{lerp}(t, L, U) = L+t\left( U-L \right) \f$. The result is clamped to the - /// specified range. - /// - /// \tparam T Type of \p t - /// \tparam Lower Type of \p lower - /// \tparam Upper Type of \p upper - /// \param t Interpolation Percentage - /// \param lower Lower bound (L) - /// \param upper Upper bound (U) - /// \return - template::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar && - !std::is_floating_point_v || - !std::is_floating_point_v || !std::is_floating_point_v, - int> = 0, - typename ST = typetraits::ScalarReturnType> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST lerp(T t, Lower lower, Upper upper) { - if (isNaN(t) || isNaN(lower) || isNaN(upper)) return std::numeric_limits::quiet_NaN(); - return static_cast(lower) + (static_cast(upper) - static_cast(lower)) * t; - } + /// \brief Linearly interpolate between two values + /// + /// \f$ \mathrm{lerp}(t, L, U) = L+t\left( U-L \right) \f$. The result is clamped to the + /// specified range. + /// + /// \tparam T Type of \p t + /// \tparam Lower Type of \p lower + /// \tparam Upper Type of \p upper + /// \param t Interpolation Percentage + /// \param lower Lower bound (L) + /// \param upper Upper bound (U) + /// \return + template> + requires(typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + !std::is_floating_point_v || + !std::is_floating_point_v || !std::is_floating_point_v) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST lerp(T t, Lower lower, Upper upper) { + if (isNaN(t) || isNaN(lower) || isNaN(upper)) return std::numeric_limits::quiet_NaN(); + return static_cast(lower) + (static_cast(upper) - static_cast(lower)) * t; + } - /// \brief Smoothly interpolate between two values - /// - /// This smooth step implementation is based on Ken Perlin's algorithm. - /// \f$ S(x)= \begin{cases}0 & x \leq 0 \\ 6 x^5-15 x^4+10 x^3 & 0 \leq x \leq 1 \\ 1 & 1 \leq - /// x\end{cases} \f$ - /// - /// This function allows you to specify a lower and upper edge, which can be used to scale - /// the range of inputs. - /// - /// \tparam T Type of \p t - /// \tparam Lower Type of \p lowerEdge - /// \tparam Upper Type of \p upperEdge - /// \param t Value to smooth step - /// \param lowerEdge At t=lowerEdge, the function returns 0 - /// \param upperEdge At t=upperEdge, the function returns 1 - /// \return \p t interpolated between \p lowerEdge and \p upperEdge - template::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar && - typetraits::TypeInfo::type == detail::LibRapidType::Scalar, - int> = 0, - typename ST = typetraits::ScalarReturnType> - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST smoothStep(T t, Lower lowerEdge = 0, - Upper upperEdge = 1) { - ST tt = clamp((t - lowerEdge) / (upperEdge - lowerEdge), 0.0, 1.0); - return tt * tt * tt * (tt * (tt * T(6) - ST(15)) + ST(10)); - } + /// \brief Smoothly interpolate between two values + /// + /// This smooth step implementation is based on Ken Perlin's algorithm. + /// \f$ S(x)= \begin{cases}0 & x \leq 0 \\ 6 x^5-15 x^4+10 x^3 & 0 \leq x \leq 1 \\ 1 & 1 \leq + /// x\end{cases} \f$ + /// + /// This function allows you to specify a lower and upper edge, which can be used to scale + /// the range of inputs. + /// + /// \tparam T Type of \p t + /// \tparam Lower Type of \p lowerEdge + /// \tparam Upper Type of \p upperEdge + /// \param t Value to smooth step + /// \param lowerEdge At t=lowerEdge, the function returns 0 + /// \param upperEdge At t=upperEdge, the function returns 1 + /// \return \p t interpolated between \p lowerEdge and \p upperEdge + template> + requires(typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar && + typetraits::TypeInfo::type == detail::LibRapidType::Scalar) + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ST smoothStep(T t, Lower lowerEdge = 0, + Upper upperEdge = 1) { + ST tt = clamp((t - lowerEdge) / (upperEdge - lowerEdge), 0.0, 1.0); + return tt * tt * tt * (tt * (tt * T(6) - ST(15)) + ST(10)); + } - /// \brief Compare the absolute and relative difference between two values, and return true if - /// they are close enough to be considered equal. - /// - /// \f$ \left| x-y \right| \leq \max\left( \mathrm{absTol}, \mathrm{relTol} \cdot \max\left( - /// \left| x \right|, \left| y \right| \right) \right) \f$ - /// - /// This is more precise than using an absolute tolerance alone, since it also takes into - /// account the magnitude of the values being compared. - /// - /// \tparam V1 Data type of the first value - /// \tparam V2 Data type of the second value - /// \tparam T Data type of the tolerance value - /// \tparam T Data type of the tolerance value - /// \param val1 First value - /// \param val2 Second value - /// \param absTol Absolute tolerance - /// \param relTol Relative tolerance - /// \return True if values are close - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE bool - isClose(const V1 &val1, const V2 &val2, const T &absTol = 1e-5, const T &relTol = 1e-5) { - return ::librapid::abs(val2 - val1) <= - ::librapid::max( - relTol * ::librapid::max(::librapid::abs(val1), ::librapid::abs(val2)), absTol); - } + /// \brief Compare the absolute and relative difference between two values, and return true if + /// they are close enough to be considered equal. + /// + /// \f$ \left| x-y \right| \leq \max\left( \mathrm{absTol}, \mathrm{relTol} \cdot \max\left( + /// \left| x \right|, \left| y \right| \right) \right) \f$ + /// + /// This is more precise than using an absolute tolerance alone, since it also takes into + /// account the magnitude of the values being compared. + /// + /// \tparam V1 Data type of the first value + /// \tparam V2 Data type of the second value + /// \tparam T Data type of the tolerance value + /// \tparam T Data type of the tolerance value + /// \param val1 First value + /// \param val2 Second value + /// \param absTol Absolute tolerance + /// \param relTol Relative tolerance + /// \return True if values are close + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE bool + isClose(const V1 &val1, const V2 &val2, const T &absTol = 1e-5, const T &relTol = 1e-5) { + return ::librapid::abs(val2 - val1) <= + ::librapid::max( + relTol * ::librapid::max(::librapid::abs(val1), ::librapid::abs(val2)), absTol); + } } // namespace librapid #endif // LIBRAPID_MATH_UTLIITY_FUNCTIONS_HPP \ No newline at end of file diff --git a/librapid/include/librapid/math/vector.hpp b/librapid/include/librapid/math/vector.hpp index d578470a..c4c4367f 100644 --- a/librapid/include/librapid/math/vector.hpp +++ b/librapid/include/librapid/math/vector.hpp @@ -41,7 +41,7 @@ namespace librapid { auto u = ::librapid::random(); // [0, 1) auto v = ::librapid::random(); // [0, 1) - auto theta = u * 2 * PI; + auto theta = u * 2 * constants::pi; auto phi = ::librapid::acos(2 * v - 1); auto r = ::librapid::cbrt(random()); auto sinTheta = ::librapid::sin(theta); diff --git a/librapid/include/librapid/math/vectorImpl.hpp b/librapid/include/librapid/math/vectorImpl.hpp index 114d1b9d..eb9567c9 100644 --- a/librapid/include/librapid/math/vectorImpl.hpp +++ b/librapid/include/librapid/math/vectorImpl.hpp @@ -874,13 +874,10 @@ namespace librapid { } \ }; \ \ - template::value || \ - typetraits::IsVector::value) && \ - (!typetraits::IsArrayContainer::value && \ - !typetraits::IsArrayContainer::value), \ - int> = 0> \ + template \ + requires((typetraits::IsVector::value || typetraits::IsVector::value) && \ + (!typetraits::IsArrayContainer::value && \ + !typetraits::IsArrayContainer::value)) \ LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator OP_(const LHS &lhs, const RHS &rhs) { \ using namespace ::librapid::vectorDetail; \ using ScalarLeft = typename typetraits::TypeInfo::Scalar; \ @@ -902,15 +899,16 @@ namespace librapid { #define VECTOR_UNARY_OP(NAME_, OP_NAME_, OP_) \ struct NAME_ { \ - template::value, int> = 0> \ + template \ + requires(typetraits::IsVector::value) \ LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator()(const Val &val) const { \ using namespace ::librapid::vectorDetail; \ return OP_(scalarExtractor(val)); \ } \ }; \ \ - template::value, int> = 0> \ + template \ + requires(typetraits::IsVector::value) \ LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto OP_NAME_(const Val &val) { \ using Op = NAME_; \ return ::librapid::vectorDetail::UnaryVecOp {val, Op {}}; \ @@ -1007,31 +1005,32 @@ namespace librapid { VECTOR_FUNC_IMPL_DEF(sqrt) VECTOR_FUNC_IMPL_DEF(cbrt) - template::value, int> = 0> + template + requires(typetraits::IsVector::value) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto mag2(const T &val) { return val.eval().storage().sum2(); } - template::value, int> = 0> + template + requires(typetraits::IsVector::value) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto mag(const T &val) { return ::librapid::sqrt(mag2(val)); } - template::value, int> = 0> + template + requires(typetraits::IsVector::value) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto sum(const T &val) { return val.eval().storage().sum(); } - template::value && typetraits::IsVector::value, int> = 0> + template + requires(typetraits::IsVector::value && typetraits::IsVector::value) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto dot(const First &first, const Second &second) { return (first * second).eval().storage().sum(); } - template::value && typetraits::IsVector::value, int> = 0> + template + requires(typetraits::IsVector::value && typetraits::IsVector::value) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto cross(const First &first, const Second &second) { LIBRAPID_ASSERT(typetraits::TypeInfo::dims == 3 && typetraits::TypeInfo::dims == 3, @@ -1050,7 +1049,8 @@ namespace librapid { return Vector {y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2}; } - template::value, int> = 0> + template + requires(typetraits::IsVector::value) LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto norm(const T &val) { return val / mag(val); }