16 #ifndef INLCUDE_MOLASSEMBLER_TEMPLE_CONSTEXPR_MATH_H
17 #define INLCUDE_MOLASSEMBLER_TEMPLE_CONSTEXPR_MATH_H
23 #include <type_traits>
26 namespace Molassembler {
31 template<
typename T,
typename U>
32 using enableIfFloatingWithReturn = std::enable_if_t<
33 std::is_floating_point<T>::value,
37 template<
typename T,
typename U>
38 using enableIfIntegralWithReturn = std::enable_if_t<
39 std::is_integral<T>::value,
43 template<
typename T,
typename U>
44 using enableIfArithmeticWithReturn = std::enable_if_t<
45 std::is_arithmetic<T>::value,
49 template<
typename FloatingPo
int>
51 std::is_floating_point<FloatingPoint>::value,
53 > isnan(
const FloatingPoint x) {
62 template<
typename ... Bools>
63 constexpr
bool XOR(Bools ... bools);
68 inline constexpr Traits::enableIfArithmeticWithReturn<T, T> abs(T x) noexcept;
72 constexpr Traits::enableIfArithmeticWithReturn<T, T>
max(T a, T b) noexcept;
76 constexpr Traits::enableIfArithmeticWithReturn<T, T>
min(T a, T b) noexcept;
82 constexpr Traits::enableIfFloatingWithReturn<T, T> toRadians(T inDegrees) noexcept;
86 constexpr Traits::enableIfFloatingWithReturn<T, T> toDegrees(T inRadians) noexcept;
89 template<
typename T,
typename U>
90 constexpr Traits::enableIfFloatingWithReturn<T, T> fmod(T value, U divider) noexcept;
94 constexpr Traits::enableIfFloatingWithReturn<T, long> ceil(T value) noexcept;
98 constexpr Traits::enableIfFloatingWithReturn<T, int> floor(T value) noexcept;
102 constexpr Traits::enableIfArithmeticWithReturn<T, T> pow(T base,
unsigned exponent) noexcept;
105 constexpr Traits::enableIfArithmeticWithReturn<T, double> pow(T base,
int exponent) noexcept;
109 constexpr Traits::enableIfFloatingWithReturn<T, T> sqrt(T x);
113 constexpr Traits::enableIfIntegralWithReturn<T, T> factorial(T x);
117 constexpr Traits::enableIfFloatingWithReturn<T, T> ln(T x);
121 constexpr Traits::enableIfFloatingWithReturn<T, T> log10(T x);
125 constexpr Traits::enableIfFloatingWithReturn<T, T> log(T x, T base);
133 constexpr Traits::enableIfFloatingWithReturn<T, T> asin(T x);
137 constexpr Traits::enableIfFloatingWithReturn<T, T> acos(T x);
141 constexpr Traits::enableIfFloatingWithReturn<T, T> atan(T x);
149 constexpr
unsigned TPPSum() {
154 template<
typename T1,
typename... T>
155 constexpr
unsigned TPPSum(T1 a, T ... pack) {
156 return a + TPPSum(pack ...);
169 throw "Ln domain error: x <= 0";
172 const T epsilon = std::numeric_limits<T>::epsilon();
180 const T iterationMultiplier = countingPower * countingPower;
190 for(
unsigned n = 3; Temple::Math::abs(previous - value) > epsilon; n += 2) {
195 countingPower *= iterationMultiplier;
198 value += 2 * countingPower / n;
210 if(!(0.0 <= x && x <= 1.0)) {
211 throw "Asin approximation domain error: only applicable for 0 < x < 1!";
215 const T x4 = x2 * x2;
219 - Math::sqrt(1 - x) * (
223 - 0.0501743046 * x2 * x
225 - 0.0170881256 * x4 * x
226 + 0.0066700901 * x4 * x2
227 - 0.0012624911 * x4 * x2 * x
234 template<
typename ... Bools>
235 constexpr
bool XOR(Bools ... bools) {
236 return Detail::TPPSum(bools ...) == 1;
240 PURITY_STRONG inline constexpr Traits::enableIfArithmeticWithReturn<T, T> abs(
const T x) noexcept {
241 return (x >= 0) ? x : -x;
245 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T>
max(
const T a,
const T b) noexcept {
246 return (a > b) ? a : b;
250 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T>
min(
const T a,
const T b) noexcept {
251 return (a < b) ? a : b;
255 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> toRadians(
const T inDegrees) noexcept {
256 return M_PI * inDegrees / 180;
260 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> toDegrees(
const T inRadians) noexcept {
261 return 180 * inRadians / M_PI;
264 template<
typename T,
typename U>
265 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> fmod(
const T value,
const U divider) noexcept {
267 while (divider < remainder) {
268 remainder -= divider;
274 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, long> ceil(
const T value) noexcept {
276 const auto truncated =
static_cast<long>(value);
280 const double eps = 1e-12;
281 if (fmod(value, 1) < eps && abs(truncated - value) < eps) {
285 if(truncated < value) {
286 return truncated + 1;
293 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, int> floor(
const T value) noexcept {
295 const auto truncated =
static_cast<int>(value);
297 if(truncated > value) {
298 return truncated - 1;
306 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> pow(
const T base,
const unsigned exponent) noexcept {
313 for(
unsigned n = 1; n < exponent; n++) {
321 PURITY_STRONG constexpr T recPow(
const T base,
const unsigned exponent) noexcept {
330 if(exponent % 2 == 0) {
331 auto halfProblem = recPow(base, exponent / 2);
332 return halfProblem * halfProblem;
335 return base * recPow(base, exponent - 1);
343 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, double> pow(
const T base,
const int exponent) noexcept {
345 return 1.0 / pow(base, static_cast<unsigned>(Temple::Math::abs(exponent)));
352 return pow(base, static_cast<unsigned>(exponent));
358 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> sqrt(
const T x) {
360 throw "Square-root domain error: Only real if x >= 0!";
363 const T epsilon = std::numeric_limits<T>::epsilon();
367 while(Temple::Math::abs(previous - value) > epsilon) {
372 value = 0.5 * (value + x / value);
379 PURITY_STRONG constexpr Traits::enableIfIntegralWithReturn<T, T> factorial(
const T x) {
381 throw "Factorial domain error!";
388 return x * factorial(x - 1);
392 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> ln(
const T x) {
393 unsigned decimalReduction = 0;
396 while(abs(calcX) > 10) {
398 decimalReduction += 1;
402 if(Math::abs(calcX / 10 - 1) < Math::abs(calcX - 1)) {
404 decimalReduction += 1;
407 return Detail::lnSeries(calcX) + decimalReduction * M_LN10;
411 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> log10(
const T x) {
413 throw "Log10 domain error!";
419 return ln(x) / M_LN10;
423 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> log(
const T x,
const T base) {
425 throw "Log domain error!";
431 return ln(x) / ln(base);
440 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> asin(
const T x) {
441 if(!(-1 <= x && x <= 1)) {
442 throw "Inverse sine domain error: only real if -1 < x < 1!";
445 if(Temple::Math::abs(x) > 0.90) {
446 return (x >= 0) ? Detail::asinApprox(x) : -Detail::asinApprox(-x);
449 const T epsilon = std::numeric_limits<T>::epsilon();
452 T upper_factorial = 1;
453 T lower_factorial = 1;
456 for(
unsigned n = 1; Temple::Math::abs(term) > epsilon; ++n) {
457 upper_factorial *= 2 * (n - 1) + 1;
458 lower_factorial *= 2 * n;
461 upper_factorial / lower_factorial
462 ) * pow(x, 2 * n + 1) / (2 * n + 1);
464 if(Traits::isnan(term)) {
475 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> acos(
const T x) {
476 if(!(-1 <= x && x <= 1)) {
477 throw "Inverse cosine domain error: only real if -1 <= x <= 1!";
480 return M_PI / 2 - asin(x);
484 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> atan(
const T x) {
485 if(!(-M_PI / 2 < x && x < M_PI / 2)) {
486 throw "Inverse cosine domain error: only real if -1 < x < 1!";
constexpr auto max(const ContainerType &container)
Composable max function. Returns the smallest value of any container.
Definition: Numeric.h:188
Defines a set of useful preprocessor macros.
#define PURITY_STRONG
Definition: Preprocessor.h:65
constexpr auto min(const ContainerType &container)
Composable min_element function. Returns the smallest value of any container.
Definition: Numeric.h:164