Move M_PI definition to math/units.h
[alexxy/gromacs.git] / src / gromacs / math / functions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2019,2020,2021, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 /*! \internal \file
38  * \brief
39  * Implements simple math functions
40  *
41  * \author Erik Lindahl <erik.lindahl@gmail.com>
42  * \ingroup module_math
43  */
44
45 #include "gmxpre.h"
46
47 #include "gromacs/math/functions.h"
48
49 #include "config.h"
50
51 #include <cstdint>
52
53 #include <array>
54 #include <limits>
55
56 #if GMX_NATIVE_WINDOWS
57 #    include <intrin.h> // _BitScanReverse, _BitScanReverse64
58 #endif
59
60 #include "gromacs/math/units.h"
61 #include "gromacs/math/utilities.h"
62 #include "gromacs/utility/gmxassert.h"
63
64 namespace gmx
65 {
66
67 unsigned int log2I(std::uint32_t n)
68 {
69     GMX_ASSERT(n > 0, "The behavior of log(0) is undefined");
70 #if HAVE_BUILTIN_CLZ
71     // gcc, clang. xor with sign bit should be optimized out
72     return __builtin_clz(n) ^ 31U;
73 #elif HAVE_BITSCANREVERSE
74     // icc, MSVC
75     {
76         unsigned long res;
77         _BitScanReverse(&res, static_cast<unsigned long>(n));
78         return static_cast<unsigned int>(res);
79     }
80 #elif HAVE_CNTLZ4
81     return 31 - __cntlz4(n);
82 #else
83     // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
84
85     static const std::array<char, 256> log2TableByte = {
86         { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
87           4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
88           5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
89           6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
90           6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
91           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
92           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
93           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
94           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }
95     };
96
97     unsigned int result;
98     unsigned int tmp1, tmp2;
99
100     if ((tmp1 = n >> 16) != 0)
101     {
102         result = ((tmp2 = tmp1 >> 8) != 0) ? 24 + log2TableByte[tmp2] : 16 + log2TableByte[tmp1];
103     }
104     else
105     {
106         result = ((tmp2 = n >> 8) != 0) ? 8 + log2TableByte[tmp2] : log2TableByte[n];
107     }
108     return result;
109 #endif
110 }
111
112
113 unsigned int log2I(std::uint64_t n)
114 {
115     GMX_ASSERT(n > 0, "The behavior of log(0) is undefined");
116 #if HAVE_BUILTIN_CLZLL
117     // gcc, icc, clang. xor with sign bit should be optimized out
118     return __builtin_clzll(n) ^ 63U;
119 #elif HAVE_BITSCANREVERSE64
120     unsigned long res;
121     _BitScanReverse64(&res, static_cast<unsigned __int64>(n));
122     return static_cast<unsigned int>(res);
123 #elif HAVE_CNTLZ8
124     return 63 - __cntlz8(n);
125 #else
126
127     // No 64-bit log2 instrinsic available. Solve it by calling our internal
128     // 32-bit version (which in turn might defer to a software solution)
129
130     std::uint32_t high32Bits = static_cast<std::uint32_t>(n >> 32);
131     std::uint32_t result;
132
133     if (high32Bits)
134     {
135         result = log2I(high32Bits) + 32;
136     }
137     else
138     {
139         result = log2I(static_cast<std::uint32_t>(n));
140     }
141
142     return result;
143 #endif
144 }
145
146 unsigned int log2I(std::int32_t n)
147 {
148     GMX_ASSERT(n > 0, "The behavior of log(n) for n<=0 is undefined");
149     return log2I(static_cast<std::uint32_t>(n));
150 }
151
152 unsigned int log2I(std::int64_t n)
153 {
154     GMX_ASSERT(n > 0, "The behavior of log(n) for n<=0 is undefined");
155     return log2I(static_cast<std::uint64_t>(n));
156 }
157
158 std::int64_t greatestCommonDivisor(std::int64_t p, std::int64_t q)
159 {
160     while (q != 0)
161     {
162         std::int64_t tmp = q;
163         q                = p % q;
164         p                = tmp;
165     }
166     return p;
167 }
168
169 double erfinv(double x)
170 {
171     double xabs = std::abs(x);
172
173     if (xabs > 1.0)
174     {
175         return std::nan("");
176     }
177
178     if (x == 1.0)
179     {
180         return std::numeric_limits<double>::infinity();
181     }
182
183     if (x == -1.0)
184     {
185         return -std::numeric_limits<double>::infinity();
186     }
187
188     double res;
189
190     if (xabs <= 0.7)
191     {
192         // Rational approximation in range [0,0.7]
193         double z = x * x;
194         double P = (((-0.140543331 * z + 0.914624893) * z - 1.645349621) * z + 0.886226899);
195         double Q = ((((0.012229801 * z - 0.329097515) * z + 1.442710462) * z - 2.118377725) * z + 1.0);
196         res      = x * P / Q;
197     }
198     else
199     {
200         // Rational approximation in range [0.7,1)
201         double z = std::sqrt(-std::log((1.0 - std::abs(x)) / 2.0));
202         double P = ((1.641345311 * z + 3.429567803) * z - 1.624906493) * z - 1.970840454;
203         double Q = (1.637067800 * z + 3.543889200) * z + 1.0;
204         res      = std::copysign(1.0, x) * P / Q;
205     }
206
207     // Double precision requires two N-R iterations
208     res = res - (std::erf(res) - x) / ((2.0 / std::sqrt(M_PI)) * std::exp(-res * res));
209     res = res - (std::erf(res) - x) / ((2.0 / std::sqrt(M_PI)) * std::exp(-res * res));
210
211     return res;
212 }
213
214 float erfinv(float x)
215 {
216     float xabs = std::abs(x);
217
218     if (xabs > 1.0F)
219     {
220         return std::nan("");
221     }
222
223     if (x == 1.0F)
224     {
225         return std::numeric_limits<float>::infinity();
226     }
227
228     if (x == -1.0F)
229     {
230         return -std::numeric_limits<float>::infinity();
231     }
232
233     float res;
234
235     if (xabs <= 0.7F)
236     {
237         // Rational approximation in range [0,0.7]
238         float z = x * x;
239         float P = (((-0.140543331F * z + 0.914624893F) * z - 1.645349621F) * z + 0.886226899F);
240         float Q = ((((0.012229801F * z - 0.329097515F) * z + 1.442710462F) * z - 2.118377725F) * z + 1.0F);
241         res = x * P / Q;
242     }
243     else
244     {
245         // Rational approximation in range [0.7,1)
246         float z = std::sqrt(-std::log((1.0 - std::abs(x)) / 2.0F));
247         float P = ((1.641345311F * z + 3.429567803F) * z - 1.624906493F) * z - 1.970840454F;
248         float Q = (1.637067800F * z + 3.543889200F) * z + 1.0F;
249         res     = std::copysign(1.0F, x) * P / Q;
250     }
251
252     // Single N-R iteration sufficient for single precision
253     res = res - (std::erf(res) - x) / ((2.0F / std::sqrt(M_PI)) * std::exp(-res * res));
254
255     return res;
256 }
257
258 } // namespace gmx