Molassembler  3.0.0
Molecule graph and conformer library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Math.h
Go to the documentation of this file.
1 
16 #ifndef INLCUDE_MOLASSEMBLER_TEMPLE_CONSTEXPR_MATH_H
17 #define INLCUDE_MOLASSEMBLER_TEMPLE_CONSTEXPR_MATH_H
18 
20 
21 #include <cmath>
22 #include <limits>
23 #include <type_traits>
24 
25 namespace Scine {
26 namespace Molassembler {
27 namespace Temple {
28 namespace Math {
29 namespace Traits {
30 
31 template<typename T, typename U>
32 using enableIfFloatingWithReturn = std::enable_if_t<
33  std::is_floating_point<T>::value,
34  U
35 >;
36 
37 template<typename T, typename U>
38 using enableIfIntegralWithReturn = std::enable_if_t<
39  std::is_integral<T>::value,
40  U
41 >;
42 
43 template<typename T, typename U>
44 using enableIfArithmeticWithReturn = std::enable_if_t<
45  std::is_arithmetic<T>::value,
46  U
47 >;
48 
49 template<typename FloatingPoint>
50 PURITY_STRONG constexpr inline std::enable_if_t<
51  std::is_floating_point<FloatingPoint>::value,
52  bool
53 > isnan(const FloatingPoint x) {
54  // see notes at http://en.cppreference.com/w/cpp/numeric/math/isnan
55  return x != x;
56 }
57 
58 } // namespace Traits
59 
60 /* Logic */
62 template<typename ... Bools>
63 constexpr bool XOR(Bools ... bools);
64 
65 /* Some very basic math functions for arithmetic types */
67 template<typename T>
68 inline constexpr Traits::enableIfArithmeticWithReturn<T, T> abs(T x) noexcept;
69 
71 template<typename T>
72 constexpr Traits::enableIfArithmeticWithReturn<T, T> max(T a, T b) noexcept;
73 
75 template<typename T>
76 constexpr Traits::enableIfArithmeticWithReturn<T, T> min(T a, T b) noexcept;
77 
78 /* Floating-point math functions */
79 
81 template<typename T>
82 constexpr Traits::enableIfFloatingWithReturn<T, T> toRadians(T inDegrees) noexcept;
83 
85 template<typename T>
86 constexpr Traits::enableIfFloatingWithReturn<T, T> toDegrees(T inRadians) noexcept;
87 
89 template<typename T, typename U>
90 constexpr Traits::enableIfFloatingWithReturn<T, T> fmod(T value, U divider) noexcept;
91 
93 template<typename T>
94 constexpr Traits::enableIfFloatingWithReturn<T, long> ceil(T value) noexcept;
95 
97 template<typename T>
98 constexpr Traits::enableIfFloatingWithReturn<T, int> floor(T value) noexcept;
99 
101 template<typename T>
102 constexpr Traits::enableIfArithmeticWithReturn<T, T> pow(T base, unsigned exponent) noexcept;
103 
104 template<typename T>
105 constexpr Traits::enableIfArithmeticWithReturn<T, double> pow(T base, int exponent) noexcept;
106 
108 template<typename T>
109 constexpr Traits::enableIfFloatingWithReturn<T, T> sqrt(T x);
110 
112 template<typename T>
113 constexpr Traits::enableIfIntegralWithReturn<T, T> factorial(T x);
114 
116 template<typename T>
117 constexpr Traits::enableIfFloatingWithReturn<T, T> ln(T x);
118 
120 template<typename T>
121 constexpr Traits::enableIfFloatingWithReturn<T, T> log10(T x);
122 
124 template<typename T>
125 constexpr Traits::enableIfFloatingWithReturn<T, T> log(T x, T base);
126 
127 // Inverse trigonometry
132 template<typename T>
133 constexpr Traits::enableIfFloatingWithReturn<T, T> asin(T x);
134 
136 template<typename T>
137 constexpr Traits::enableIfFloatingWithReturn<T, T> acos(T x);
138 
140 template<typename T>
141 constexpr Traits::enableIfFloatingWithReturn<T, T> atan(T x);
142 
143 
144 /* Implementations begin here ------------------------------------------------*/
145 
146 namespace Detail { // Implementation helpers
147 
148 // Specialization of TPPSum for empty parameter pack
149 constexpr unsigned TPPSum() {
150  return 0;
151 }
152 
153 // Template parameter pack sum (needed for XOR function)
154 template<typename T1, typename... T>
155 constexpr unsigned TPPSum(T1 a, T ... pack) {
156  return a + TPPSum(pack ...);
157 }
158 
159 /* Based on series expansion of ln x:
160  *
161  * y(x) = (x - 1) / (x + 1)
162  * ln x = 2 [ y + y^3/3 + y^5/5 + ...]
163  *
164  * for Re x ≥ 0 and x ≠ 0
165  */
166 template<typename T>
167 PURITY_STRONG constexpr T lnSeries(const T x) {
168  if(x <= 0) {
169  throw "Ln domain error: x <= 0";
170  }
171 
172  const T epsilon = std::numeric_limits<T>::epsilon();
173 
174  // Since every term, y is multiplied by y², we keep a counting square
175  T countingPower { // at n = 1
176  (x - 1) / (x + 1)
177  };
178 
179  // This is just y², which is what we multiply which each term
180  const T iterationMultiplier = countingPower * countingPower;
181 
182  // Initial value for n = 1 (terms up until y)
183  T value {
184  2 * countingPower
185  };
186 
187  // Term n = 0
188  T previous {0};
189 
190  for(unsigned n = 3; Temple::Math::abs(previous - value) > epsilon; n += 2) {
191  // previous iteration
192  previous = value;
193 
194  // Update the power: y^(n-2) * y^2 becomes y^n
195  countingPower *= iterationMultiplier;
196 
197  // Add the full term to the running value
198  value += 2 * countingPower / n;
199  }
200 
201  return value;
202 }
203 
204 /* Implements an approximation to asin over 0 < x < 1 with accuracy > 2e-8
205  * from Abramowitz, M., Stegun, I.: Handbook of Mathematical Functions, p. 64,
206  * 1964, from http://people.math.sfu.ca/~cbm/aands/abramowitz_and_stegun.pdf
207  */
208 template<typename T>
209 PURITY_STRONG constexpr T asinApprox(const T x) {
210  if(!(0.0 <= x && x <= 1.0)) {
211  throw "Asin approximation domain error: only applicable for 0 < x < 1!";
212  }
213 
214  const T x2 = x * x;
215  const T x4 = x2 * x2;
216 
217  return (
218  M_PI / 2
219  - Math::sqrt(1 - x) * (
220  1.5707963050
221  - 0.2145988016 * x
222  + 0.0889789874 * x2
223  - 0.0501743046 * x2 * x
224  + 0.0308918810 * x4
225  - 0.0170881256 * x4 * x
226  + 0.0066700901 * x4 * x2
227  - 0.0012624911 * x4 * x2 * x
228  )
229  );
230 }
231 
232 } // namespace Detail
233 
234 template<typename ... Bools>
235 constexpr bool XOR(Bools ... bools) {
236  return Detail::TPPSum(bools ...) == 1;
237 }
238 
239 template<typename T>
240 PURITY_STRONG inline constexpr Traits::enableIfArithmeticWithReturn<T, T> abs(const T x) noexcept {
241  return (x >= 0) ? x : -x;
242 }
243 
244 template<typename T>
245 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> max(const T a, const T b) noexcept {
246  return (a > b) ? a : b;
247 }
248 
249 template<typename T>
250 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> min(const T a, const T b) noexcept {
251  return (a < b) ? a : b;
252 }
253 
254 template<typename T>
255 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> toRadians(const T inDegrees) noexcept {
256  return M_PI * inDegrees / 180;
257 }
258 
259 template<typename T>
260 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> toDegrees(const T inRadians) noexcept {
261  return 180 * inRadians / M_PI;
262 }
263 
264 template<typename T, typename U>
265 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> fmod(const T value, const U divider) noexcept {
266  T remainder = value;
267  while (divider < remainder) {
268  remainder -= divider;
269  }
270  return remainder;
271 }
272 
273 template<typename T>
274 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, long> ceil(const T value) noexcept {
275  // Truncate to an integer
276  const auto truncated = static_cast<long>(value);
277 
278  // we first check if the given floating point number is actually an integer
279  // this avoids rounding mistakes, occuring with some compilers
280  const double eps = 1e-12;
281  if (fmod(value, 1) < eps && abs(truncated - value) < eps) {
282  return truncated;
283  }
284 
285  if(truncated < value) {
286  return truncated + 1;
287  }
288 
289  return truncated;
290 }
291 
292 template<typename T>
293 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, int> floor(const T value) noexcept {
294  // Truncate to an int
295  const auto truncated = static_cast<int>(value);
296 
297  if(truncated > value) {
298  return truncated - 1;
299  }
300 
301  return truncated;
302 }
303 
304 // Really weak first implementation
305 template<typename T>
306 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> pow(const T base, const unsigned exponent) noexcept {
307  if(exponent == 0) {
308  return 1;
309  }
310 
311  double value = base;
312 
313  for(unsigned n = 1; n < exponent; n++) {
314  value *= base;
315  }
316 
317  return value;
318 }
319 
320 template<typename T>
321 PURITY_STRONG constexpr T recPow(const T base, const unsigned exponent) noexcept {
322  if(exponent == 0) {
323  return 1;
324  }
325 
326  if(exponent == 1) {
327  return base;
328  }
329 
330  if(exponent % 2 == 0) {
331  auto halfProblem = recPow(base, exponent / 2);
332  return halfProblem * halfProblem;
333  }
334 
335  return base * recPow(base, exponent - 1);
336 }
337 
342 template<typename T>
343 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, double> pow(const T base, const int exponent) noexcept {
344  if(exponent < 0) {
345  return 1.0 / pow(base, static_cast<unsigned>(Temple::Math::abs(exponent)));
346  }
347 
348  if(exponent == 0) {
349  return 1;
350  }
351 
352  return pow(base, static_cast<unsigned>(exponent));
353 }
354 
355 /* Implements Newton's iteration to compute the square root of a positive number
356  */
357 template<typename T>
358 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> sqrt(const T x) {
359  if(x < 0) {
360  throw "Square-root domain error: Only real if x >= 0!";
361  }
362 
363  const T epsilon = std::numeric_limits<T>::epsilon();
364  T value = 1;
365  T previous = 2;
366 
367  while(Temple::Math::abs(previous - value) > epsilon) {
368  // store the previous value
369  previous = value;
370 
371  // compute next iteration
372  value = 0.5 * (value + x / value);
373  }
374 
375  return value;
376 }
377 
378 template<typename T>
379 PURITY_STRONG constexpr Traits::enableIfIntegralWithReturn<T, T> factorial(const T x) {
380  if(x < 0) {
381  throw "Factorial domain error!";
382  }
383 
384  if(x == 0) {
385  return 1;
386  }
387 
388  return x * factorial(x - 1);
389 }
390 
391 template<typename T>
392 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> ln(const T x) {
393  unsigned decimalReduction = 0;
394  T calcX = x;
395 
396  while(abs(calcX) > 10) {
397  calcX /= 10;
398  decimalReduction += 1;
399  }
400 
401  // Ensure last division leads to value closer to 1
402  if(Math::abs(calcX / 10 - 1) < Math::abs(calcX - 1)) {
403  calcX /= 10;
404  decimalReduction += 1;
405  }
406 
407  return Detail::lnSeries(calcX) + decimalReduction * M_LN10;
408 }
409 
410 template<typename T>
411 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> log10(const T x) {
412  if(x <= 0) {
413  throw "Log10 domain error!";
414  }
415 
416  /* ln(z) = ln(10) * log10(z)
417  * -> log10(z) = ln(z) / ln(10)
418  */
419  return ln(x) / M_LN10;
420 }
421 
422 template<typename T>
423 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> log(const T x, const T base) {
424  if(x <= 0) {
425  throw "Log domain error!";
426  }
427 
428  /* ln(z) = ln(b) * log_b(z)
429  * -> log_b(z) = ln(z) / ln(b)
430  */
431  return ln(x) / ln(base);
432 }
433 
434 /* Implements the infinite series where the derivative is expanded as a binomial
435  * series and every term is integrated. Deviates most strongly from std::asin at
436  * values very close to the borders. Perhaps it is best to use the approximate
437  * form there?
438  */
439 template<typename T>
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!";
443  }
444 
445  if(Temple::Math::abs(x) > 0.90) {
446  return (x >= 0) ? Detail::asinApprox(x) : -Detail::asinApprox(-x);
447  }
448 
449  const T epsilon = std::numeric_limits<T>::epsilon();
450 
451  T value = x;
452  T upper_factorial = 1;
453  T lower_factorial = 1;
454  T term = 1;
455 
456  for(unsigned n = 1; Temple::Math::abs(term) > epsilon; ++n) {
457  upper_factorial *= 2 * (n - 1) + 1;
458  lower_factorial *= 2 * n;
459 
460  term = (
461  upper_factorial / lower_factorial
462  ) * pow(x, 2 * n + 1) / (2 * n + 1);
463 
464  if(Traits::isnan(term)) {
465  break;
466  }
467 
468  value += term;
469  }
470 
471  return value;
472 }
473 
474 template<typename T>
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!";
478  }
479 
480  return M_PI / 2 - asin(x);
481 }
482 
483 template<typename T>
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!";
487  }
488 
489  return asin(
490  x / sqrt(x * x + 1)
491  );
492 }
493 
494 } // namespace Math
495 } // namespace Temple
496 } // namespace Molassembler
497 } // namespace Scine
498 
499 #endif
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