Molassembler  1.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>
90 constexpr Traits::enableIfFloatingWithReturn<T, int> ceil(T value) noexcept;
91 
93 template<typename T>
94 constexpr Traits::enableIfFloatingWithReturn<T, int> floor(T value) noexcept;
95 
97 template<typename T>
98 constexpr Traits::enableIfArithmeticWithReturn<T, T> pow(T base, unsigned exponent) noexcept;
99 
100 template<typename T>
101 constexpr Traits::enableIfArithmeticWithReturn<T, double> pow(T base, int exponent) noexcept;
102 
104 template<typename T>
105 constexpr Traits::enableIfFloatingWithReturn<T, T> sqrt(T x);
106 
108 template<typename T>
109 constexpr Traits::enableIfIntegralWithReturn<T, T> factorial(T x);
110 
112 template<typename T>
113 constexpr Traits::enableIfFloatingWithReturn<T, T> ln(T x);
114 
116 template<typename T>
117 constexpr Traits::enableIfFloatingWithReturn<T, T> log10(T x);
118 
120 template<typename T>
121 constexpr Traits::enableIfFloatingWithReturn<T, T> log(T x, T base);
122 
123 // Inverse trigonometry
128 template<typename T>
129 constexpr Traits::enableIfFloatingWithReturn<T, T> asin(T x);
130 
132 template<typename T>
133 constexpr Traits::enableIfFloatingWithReturn<T, T> acos(T x);
134 
136 template<typename T>
137 constexpr Traits::enableIfFloatingWithReturn<T, T> atan(T x);
138 
139 
140 /* Implementations begin here ------------------------------------------------*/
141 
142 namespace Detail { // Implementation helpers
143 
144 // Specialization of TPPSum for empty parameter pack
145 constexpr unsigned TPPSum() {
146  return 0;
147 }
148 
149 // Template parameter pack sum (needed for XOR function)
150 template<typename T1, typename... T>
151 constexpr unsigned TPPSum(T1 a, T ... pack) {
152  return a + TPPSum(pack ...);
153 }
154 
155 /* Based on series expansion of ln x:
156  *
157  * y(x) = (x - 1) / (x + 1)
158  * ln x = 2 [ y + y^3/3 + y^5/5 + ...]
159  *
160  * for Re x ≥ 0 and x ≠ 0
161  */
162 template<typename T>
163 PURITY_STRONG constexpr T lnSeries(const T x) {
164  if(x <= 0) {
165  throw "Ln domain error: x <= 0";
166  }
167 
168  const T epsilon = std::numeric_limits<T>::epsilon();
169 
170  // Since every term, y is multiplied by y², we keep a counting square
171  T countingPower { // at n = 1
172  (x - 1) / (x + 1)
173  };
174 
175  // This is just y², which is what we multiply which each term
176  const T iterationMultiplier = countingPower * countingPower;
177 
178  // Initial value for n = 1 (terms up until y)
179  T value {
180  2 * countingPower
181  };
182 
183  // Term n = 0
184  T previous {0};
185 
186  for(unsigned n = 3; Temple::Math::abs(previous - value) > epsilon; n += 2) {
187  // previous iteration
188  previous = value;
189 
190  // Update the power: y^(n-2) * y^2 becomes y^n
191  countingPower *= iterationMultiplier;
192 
193  // Add the full term to the running value
194  value += 2 * countingPower / n;
195  }
196 
197  return value;
198 }
199 
200 /* Implements an approximation to asin over 0 < x < 1 with accuracy > 2e-8
201  * from Abramowitz, M., Stegun, I.: Handbook of Mathematical Functions, p. 64,
202  * 1964, from http://people.math.sfu.ca/~cbm/aands/abramowitz_and_stegun.pdf
203  */
204 template<typename T>
205 PURITY_STRONG constexpr T asinApprox(const T x) {
206  if(!(0.0 <= x && x <= 1.0)) {
207  throw "Asin approximation domain error: only applicable for 0 < x < 1!";
208  }
209 
210  const T x2 = x * x;
211  const T x4 = x2 * x2;
212 
213  return (
214  M_PI / 2
215  - Math::sqrt(1 - x) * (
216  1.5707963050
217  - 0.2145988016 * x
218  + 0.0889789874 * x2
219  - 0.0501743046 * x2 * x
220  + 0.0308918810 * x4
221  - 0.0170881256 * x4 * x
222  + 0.0066700901 * x4 * x2
223  - 0.0012624911 * x4 * x2 * x
224  )
225  );
226 }
227 
228 } // namespace Detail
229 
230 template<typename ... Bools>
231 constexpr bool XOR(Bools ... bools) {
232  return Detail::TPPSum(bools ...) == 1;
233 }
234 
235 template<typename T>
236 PURITY_STRONG inline constexpr Traits::enableIfArithmeticWithReturn<T, T> abs(const T x) noexcept {
237  return (x >= 0) ? x : -x;
238 }
239 
240 template<typename T>
241 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> max(const T a, const T b) noexcept {
242  return (a > b) ? a : b;
243 }
244 
245 template<typename T>
246 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> min(const T a, const T b) noexcept {
247  return (a < b) ? a : b;
248 }
249 
250 template<typename T>
251 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> toRadians(const T inDegrees) noexcept {
252  return M_PI * inDegrees / 180;
253 }
254 
255 template<typename T>
256 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> toDegrees(const T inRadians) noexcept {
257  return 180 * inRadians / M_PI;
258 }
259 
260 template<typename T>
261 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, int> ceil(const T value) noexcept {
262  // Truncate to an int
263  const auto truncated = static_cast<int>(value);
264 
265  if(truncated < value) {
266  return truncated + 1;
267  }
268 
269  return truncated;
270 }
271 
272 template<typename T>
273 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, int> floor(const T value) noexcept {
274  // Truncate to an int
275  const auto truncated = static_cast<int>(value);
276 
277  if(truncated > value) {
278  return truncated - 1;
279  }
280 
281  return truncated;
282 }
283 
284 // Really weak first implementation
285 template<typename T>
286 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, T> pow(const T base, const unsigned exponent) noexcept {
287  if(exponent == 0) {
288  return 1;
289  }
290 
291  double value = base;
292 
293  for(unsigned n = 1; n < exponent; n++) {
294  value *= base;
295  }
296 
297  return value;
298 }
299 
300 template<typename T>
301 PURITY_STRONG constexpr T recPow(const T base, const unsigned exponent) noexcept {
302  if(exponent == 0) {
303  return 1;
304  }
305 
306  if(exponent == 1) {
307  return base;
308  }
309 
310  if(exponent % 2 == 0) {
311  auto halfProblem = recPow(base, exponent / 2);
312  return halfProblem * halfProblem;
313  }
314 
315  return base * recPow(base, exponent - 1);
316 }
317 
322 template<typename T>
323 PURITY_STRONG constexpr Traits::enableIfArithmeticWithReturn<T, double> pow(const T base, const int exponent) noexcept {
324  if(exponent < 0) {
325  return 1.0 / pow(base, static_cast<unsigned>(Temple::Math::abs(exponent)));
326  }
327 
328  if(exponent == 0) {
329  return 1;
330  }
331 
332  return pow(base, static_cast<unsigned>(exponent));
333 }
334 
335 /* Implements Newton's iteration to compute the square root of a positive number
336  */
337 template<typename T>
338 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> sqrt(const T x) {
339  if(x < 0) {
340  throw "Square-root domain error: Only real if x >= 0!";
341  }
342 
343  const T epsilon = std::numeric_limits<T>::epsilon();
344  T value = 1;
345  T previous = 2;
346 
347  while(Temple::Math::abs(previous - value) > epsilon) {
348  // store the previous value
349  previous = value;
350 
351  // compute next iteration
352  value = 0.5 * (value + x / value);
353  }
354 
355  return value;
356 }
357 
358 template<typename T>
359 PURITY_STRONG constexpr Traits::enableIfIntegralWithReturn<T, T> factorial(const T x) {
360  if(x < 0) {
361  throw "Factorial domain error!";
362  }
363 
364  if(x == 0) {
365  return 1;
366  }
367 
368  return x * factorial(x - 1);
369 }
370 
371 template<typename T>
372 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> ln(const T x) {
373  unsigned decimalReduction = 0;
374  T calcX = x;
375 
376  while(abs(calcX) > 10) {
377  calcX /= 10;
378  decimalReduction += 1;
379  }
380 
381  // Ensure last division leads to value closer to 1
382  if(Math::abs(calcX / 10 - 1) < Math::abs(calcX - 1)) {
383  calcX /= 10;
384  decimalReduction += 1;
385  }
386 
387  return Detail::lnSeries(calcX) + decimalReduction * M_LN10;
388 }
389 
390 template<typename T>
391 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> log10(const T x) {
392  if(x <= 0) {
393  throw "Log10 domain error!";
394  }
395 
396  /* ln(z) = ln(10) * log10(z)
397  * -> log10(z) = ln(z) / ln(10)
398  */
399  return ln(x) / M_LN10;
400 }
401 
402 template<typename T>
403 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> log(const T x, const T base) {
404  if(x <= 0) {
405  throw "Log domain error!";
406  }
407 
408  /* ln(z) = ln(b) * log_b(z)
409  * -> log_b(z) = ln(z) / ln(b)
410  */
411  return ln(x) / ln(base);
412 }
413 
414 /* Implements the infinite series where the derivative is expanded as a binomial
415  * series and every term is integrated. Deviates most strongly from std::asin at
416  * values very close to the borders. Perhaps it is best to use the approximate
417  * form there?
418  */
419 template<typename T>
420 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> asin(const T x) {
421  if(!(-1 <= x && x <= 1)) {
422  throw "Inverse sine domain error: only real if -1 < x < 1!";
423  }
424 
425  if(Temple::Math::abs(x) > 0.90) {
426  return (x >= 0) ? Detail::asinApprox(x) : -Detail::asinApprox(-x);
427  }
428 
429  const T epsilon = std::numeric_limits<T>::epsilon();
430 
431  T value = x;
432  T upper_factorial = 1;
433  T lower_factorial = 1;
434  T term = 1;
435 
436  for(unsigned n = 1; Temple::Math::abs(term) > epsilon; ++n) {
437  upper_factorial *= 2 * (n - 1) + 1;
438  lower_factorial *= 2 * n;
439 
440  term = (
441  upper_factorial / lower_factorial
442  ) * pow(x, 2 * n + 1) / (2 * n + 1);
443 
444  if(Traits::isnan(term)) {
445  break;
446  }
447 
448  value += term;
449  }
450 
451  return value;
452 }
453 
454 template<typename T>
455 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> acos(const T x) {
456  if(!(-1 <= x && x <= 1)) {
457  throw "Inverse cosine domain error: only real if -1 <= x <= 1!";
458  }
459 
460  return M_PI / 2 - asin(x);
461 }
462 
463 template<typename T>
464 PURITY_STRONG constexpr Traits::enableIfFloatingWithReturn<T, T> atan(const T x) {
465  if(!(-M_PI / 2 < x && x < M_PI / 2)) {
466  throw "Inverse cosine domain error: only real if -1 < x < 1!";
467  }
468 
469  return asin(
470  x / sqrt(x * x + 1)
471  );
472 }
473 
474 } // namespace Math
475 } // namespace Temple
476 } // namespace Molassembler
477 } // namespace Scine
478 
479 #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