Establish `api/` as the home for installed headers.
[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, 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/utilities.h"
61 #include "gromacs/utility/gmxassert.h"
62
63 namespace gmx
64 {
65
66 unsigned int log2I(std::uint32_t n)
67 {
68     GMX_ASSERT(n > 0, "The behavior of log(0) is undefined");
69 #if HAVE_BUILTIN_CLZ
70     // gcc, clang. xor with sign bit should be optimized out
71     return __builtin_clz(n) ^ 31U;
72 #elif HAVE_BITSCANREVERSE
73     // icc, MSVC
74     {
75         unsigned long res;
76         _BitScanReverse(&res, static_cast<unsigned long>(n));
77         return static_cast<unsigned int>(res);
78     }
79 #elif HAVE_CNTLZ4
80     return 31 - __cntlz4(n);
81 #else
82     // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
83
84     static const std::array<char, 256> log2TableByte = {
85         { 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,
86           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,
87           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,
88           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,
89           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,
90           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,
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 }
94     };
95
96     unsigned int result;
97     unsigned int tmp1, tmp2;
98
99     if ((tmp1 = n >> 16) != 0)
100     {
101         result = ((tmp2 = tmp1 >> 8) != 0) ? 24 + log2TableByte[tmp2] : 16 + log2TableByte[tmp1];
102     }
103     else
104     {
105         result = ((tmp2 = n >> 8) != 0) ? 8 + log2TableByte[tmp2] : log2TableByte[n];
106     }
107     return result;
108 #endif
109 }
110
111
112 unsigned int log2I(std::uint64_t n)
113 {
114     GMX_ASSERT(n > 0, "The behavior of log(0) is undefined");
115 #if HAVE_BUILTIN_CLZLL
116     // gcc, icc, clang. xor with sign bit should be optimized out
117     return __builtin_clzll(n) ^ 63U;
118 #elif HAVE_BITSCANREVERSE64
119     unsigned long res;
120     _BitScanReverse64(&res, static_cast<unsigned __int64>(n));
121     return static_cast<unsigned int>(res);
122 #elif HAVE_CNTLZ8
123     return 63 - __cntlz8(n);
124 #else
125
126     // No 64-bit log2 instrinsic available. Solve it by calling our internal
127     // 32-bit version (which in turn might defer to a software solution)
128
129     std::uint32_t high32Bits = static_cast<std::uint32_t>(n >> 32);
130     std::uint32_t result;
131
132     if (high32Bits)
133     {
134         result = log2I(high32Bits) + 32;
135     }
136     else
137     {
138         result = log2I(static_cast<std::uint32_t>(n));
139     }
140
141     return result;
142 #endif
143 }
144
145 unsigned int log2I(std::int32_t n)
146 {
147     GMX_ASSERT(n > 0, "The behavior of log(n) for n<=0 is undefined");
148     return log2I(static_cast<std::uint32_t>(n));
149 }
150
151 unsigned int log2I(std::int64_t n)
152 {
153     GMX_ASSERT(n > 0, "The behavior of log(n) for n<=0 is undefined");
154     return log2I(static_cast<std::uint64_t>(n));
155 }
156
157 std::int64_t greatestCommonDivisor(std::int64_t p, std::int64_t q)
158 {
159     while (q != 0)
160     {
161         std::int64_t tmp = q;
162         q                = p % q;
163         p                = tmp;
164     }
165     return p;
166 }
167
168 double erfinv(double x)
169 {
170     double xabs = std::abs(x);
171
172     if (xabs > 1.0)
173     {
174         return std::nan("");
175     }
176
177     if (x == 1.0)
178     {
179         return std::numeric_limits<double>::infinity();
180     }
181
182     if (x == -1.0)
183     {
184         return -std::numeric_limits<double>::infinity();
185     }
186
187     double res;
188
189     if (xabs <= 0.7)
190     {
191         // Rational approximation in range [0,0.7]
192         double z = x * x;
193         double P = (((-0.140543331 * z + 0.914624893) * z - 1.645349621) * z + 0.886226899);
194         double Q = ((((0.012229801 * z - 0.329097515) * z + 1.442710462) * z - 2.118377725) * z + 1.0);
195         res      = x * P / Q;
196     }
197     else
198     {
199         // Rational approximation in range [0.7,1)
200         double z = std::sqrt(-std::log((1.0 - std::abs(x)) / 2.0));
201         double P = ((1.641345311 * z + 3.429567803) * z - 1.624906493) * z - 1.970840454;
202         double Q = (1.637067800 * z + 3.543889200) * z + 1.0;
203         res      = std::copysign(1.0, x) * P / Q;
204     }
205
206     // Double precision requires two N-R iterations
207     res = res - (std::erf(res) - x) / ((2.0 / std::sqrt(M_PI)) * std::exp(-res * res));
208     res = res - (std::erf(res) - x) / ((2.0 / std::sqrt(M_PI)) * std::exp(-res * res));
209
210     return res;
211 }
212
213 float erfinv(float x)
214 {
215     float xabs = std::abs(x);
216
217     if (xabs > 1.0F)
218     {
219         return std::nan("");
220     }
221
222     if (x == 1.0F)
223     {
224         return std::numeric_limits<float>::infinity();
225     }
226
227     if (x == -1.0F)
228     {
229         return -std::numeric_limits<float>::infinity();
230     }
231
232     float res;
233
234     if (xabs <= 0.7F)
235     {
236         // Rational approximation in range [0,0.7]
237         float z = x * x;
238         float P = (((-0.140543331F * z + 0.914624893F) * z - 1.645349621F) * z + 0.886226899F);
239         float Q = ((((0.012229801F * z - 0.329097515F) * z + 1.442710462F) * z - 2.118377725F) * z + 1.0F);
240         res = x * P / Q;
241     }
242     else
243     {
244         // Rational approximation in range [0.7,1)
245         float z = std::sqrt(-std::log((1.0 - std::abs(x)) / 2.0F));
246         float P = ((1.641345311F * z + 3.429567803F) * z - 1.624906493F) * z - 1.970840454F;
247         float Q = (1.637067800F * z + 3.543889200F) * z + 1.0F;
248         res     = std::copysign(1.0F, x) * P / Q;
249     }
250
251     // Single N-R iteration sufficient for single precision
252     res = res - (std::erf(res) - x) / ((2.0F / std::sqrt(M_PI)) * std::exp(-res * res));
253
254     return res;
255 }
256
257 } // namespace gmx