The Sparta Modeling Framework
Loading...
Searching...
No Matches
MathUtils.hpp
1// <MathUtils.hpp> -*- C++ -*-
2
3#pragma once
4
5#include <cmath>
6#include <cinttypes>
7#include <typeinfo>
8#include <cassert>
9#include <typeinfo>
10#include <type_traits>
11#include <random>
12
15
16namespace sparta {
17 namespace utils {
18
19 inline double log2 (double x) {
20 // double y = std::log(x) / std::log(2.0);
21 // return y;
22 return std::log2(x);
23 }
24
25 template <class T>
26 inline uint32_t log2_lsb(const T& x)
27 {
28 (void) x;
29 //bool UNSUPPORTED_TYPE = false;
30 throw SpartaException("Unsupported type for log2_lsb: ") << typeid(T).name();
31 }
32
33 template <>
34 inline uint32_t log2_lsb<uint32_t>(const uint32_t &x)
35 {
36 // This uses a DeBrujin sequence to find the index
37 // (0..31) of the least significant bit in iclass
38 // Refer to Leiserson's bitscan algorithm:
39 // http://chessprogramming.wikispaces.com/BitScan
40 static const uint32_t index32[32] = {
41 0, 1, 28, 2, 29, 14, 24, 3,
42 30, 22, 20, 15, 25, 17, 4, 8,
43 31, 27, 13, 23, 21, 19, 16, 7,
44 26, 12, 18, 6, 11, 5, 10, 9
45 };
46
47 static const uint32_t debruijn32 = 0x077CB531u;
48
49 return index32[((x & -x) * debruijn32) >> 27];
50 }
51
52 template <>
53 inline uint32_t log2_lsb<uint64_t>(const uint64_t &x)
54 {
55 // This uses a DeBrujin sequence to find the index
56 // (0..63) of the least significant bit in iclass
57 // Refer to Leiserson's bitscan algorithm:
58 // http://chessprogramming.wikispaces.com/BitScan
59 static const uint32_t index64[64] = {
60 63, 0, 58, 1, 59, 47, 53, 2,
61 60, 39, 48, 27, 54, 33, 42, 3,
62 61, 51, 37, 40, 49, 18, 28, 20,
63 55, 30, 34, 11, 43, 14, 22, 4,
64 62, 57, 46, 52, 38, 26, 32, 41,
65 50, 36, 17, 19, 29, 10, 13, 21,
66 56, 45, 25, 31, 35, 16, 9, 12,
67 44, 24, 15, 8, 23, 7, 6, 5
68 };
69
70 static const uint64_t debruijn64 = 0x07EDD5E59A4E28C2ull;
71
72 return index64[((x & -x) * debruijn64) >> 58];
73 }
74
75 template <class T>
76 inline uint64_t floor_log2(T x)
77 {
78 // floor_log2(x) is the index of the most-significant 1 bit of x.
79 // (This is the old iterative version)
80 // NOTE: This function returns 0 for log2(0), but mathematically, it should be undefined
81 // throw SpartaException("floor_log2(0) is undefined");
82 uint64_t y = 0;
83 while (x >>= 1) {
84 y++;
85 }
86 return y;
87 }
88
89 template<>
90 inline uint64_t floor_log2<double>(double x)
91 {
92 return std::floor(log2(x));
93 }
94
95 template <>
96 inline uint64_t floor_log2<uint32_t>(uint32_t x)
97 {
98 if (x == 0) {
99 // NOTE: This function returns 0 for log2(0) for compatibility with the old version,
100 // but mathematically, it should be undefined
101 // throw SpartaException("floor_log2(0) is undefined");
102 return 0;
103 }
104
105 // This is a fast floor(log2(x)) based on DeBrujin's algorithm
106 // (based on generally available and numerous sources)
107 static const uint64_t lut[] = {
108 0, 9, 1, 10, 13, 21, 2, 29,
109 11, 14, 16, 18, 22, 25, 3, 30,
110 8, 12, 20, 28, 15, 17, 24, 7,
111 19, 27, 23, 6, 26, 5, 4, 31};
112
113 x |= x >> 1;
114 x |= x >> 2;
115 x |= x >> 4;
116 x |= x >> 8;
117 x |= x >> 16;
118 return lut[(uint32_t)(x * 0x07C4ACDDul) >> 27];
119 }
120
121 template <>
122 inline uint64_t floor_log2<uint64_t>(uint64_t x)
123 {
124 if (x == 0) {
125 // NOTE: This function returns 0 for log2(0) for compatibility with the old version,
126 // but mathematically, it should be undefined
127 // throw SpartaException("floor_log2(0) is undefined");
128 return 0;
129 }
130
131 // This is a fast floor(log2(x)) based on DeBrujin's algorithm
132 // (based on generally available and numerous sources)
133 static const uint64_t lut[] = {
134 63, 0, 58, 1, 59, 47, 53, 2,
135 60, 39, 48, 27, 54, 33, 42, 3,
136 61, 51, 37, 40, 49, 18, 28, 20,
137 55, 30, 34, 11, 43, 14, 22, 4,
138 62, 57, 46, 52, 38, 26, 32, 41,
139 50, 36, 17, 19, 29, 10, 13, 21,
140 56, 45, 25, 31, 35, 16, 9, 12,
141 44, 24, 15, 8, 23, 7, 6, 5};
142
143 x |= x >> 1;
144 x |= x >> 2;
145 x |= x >> 4;
146 x |= x >> 8;
147 x |= x >> 16;
148 x |= x >> 32;
149 return lut[((uint64_t)((x - (x >> 1)) * 0x07EDD5E59A4E28C2ull)) >> 58];
150 }
151
152 inline uint64_t ceil_log2 (uint64_t x)
153 {
154 // If x is a power of 2 then ceil_log2(x) is floor_log2(x).
155 // Otherwise ceil_log2(x) is floor_log2(x) + 1.
156 uint64_t y = floor_log2(x);
157 if ((static_cast<uint64_t>(1) << y) != x) {
158 y++;
159 }
160 return y;
161 }
162
163 inline uint64_t pow2 (uint64_t x) {
164 uint64_t y = static_cast<uint64_t>(1) << x;
165 return y;
166 }
167
168 inline bool is_power_of_2 (uint64_t x) {
169 bool y = x && !(x & (x - 1));
170 return y;
171 }
172
173 inline uint64_t next_power_of_2(uint64_t v)
174 {
175 if(v < 2) {
176 return 1ull;
177 }
178 return 1ull << ((sizeof(uint64_t) * 8) - __builtin_clzll(v - 1ull));
179 }
180
181 inline uint64_t ones (uint64_t x) {
182 uint64_t y = (static_cast<uint64_t>(1) << x) - 1;
183 return y;
184 }
185
186 // Be careful of the types, here. The default
187 // version of abs() expects to return the same type
188 // as the given argument x. This is ok for float, double, eg.
189 // but not for unsigned integer types, since the x < 0 test
190 // will always be false.
191 template <class T>
192 inline T abs(T x)
193 {
194 return (x < 0 ? -x : x);
195 }
196
197 template <>
198 inline uint8_t abs<uint8_t>(uint8_t x) {
199 uint8_t sign_mask = int8_t(x) >> 7;
200 return (x + sign_mask) ^ sign_mask;
201 }
202
203 template <>
204 inline uint16_t abs<uint16_t>(uint16_t x) {
205 uint16_t sign_mask = int16_t(x) >> 15;
206 return (x + sign_mask) ^ sign_mask;
207 }
208
209 template <>
210 inline uint32_t abs<uint32_t>(uint32_t x) {
211 uint32_t sign_mask = int32_t(x) >> 31;
212 return (x + sign_mask) ^ sign_mask;
213 }
214
215 template <>
216 inline uint64_t abs<uint64_t>(uint64_t x) {
217 uint64_t sign_mask = int64_t(x) >> 63;
218 return (x + sign_mask) ^ sign_mask;
219 }
220
221 template <class T>
222 inline T gcd(T u, T v)
223 {
224 (void) u;
225 (void) v;
226 static_assert("This is an unsupported type");
227 }
228
229 // Adapted from WIKI article on binary GCD algorithm
230 template <>
231 inline uint32_t gcd<uint32_t>(uint32_t u, uint32_t v)
232 {
233 // GCD(0,x) == GCD(x,0) == x
234 if (u == 0 || v == 0)
235 return u | v;
236
237 // Let shift := lg K, where K is the greatest power of 2
238 // dividing both u and v.
239 uint32_t shift = log2_lsb(u | v);
240
241 u >>= shift;
242 u >>= log2_lsb(u);
243
244 // From here on, u is always odd.
245 v >>= shift;
246 do {
247 v >>= log2_lsb(v);
248
249 // Now u and v are both odd. Swap if necessary so u <= v,
250 // then set v = v - u (which is even). For bignums, the
251 // swapping is just pointer movement, and the subtraction
252 // can be done in-place.
253 if (u > v) {
254 uint32_t t = u;
255 u = v;
256 v = t;
257 }
258 v = v - u;
259 } while (v != 0);
260
261 return u << shift;
262 }
263
264 // Adapted from WIKI article on binary GCD algorithm
265 template <>
266 inline uint64_t gcd<uint64_t>(uint64_t u, uint64_t v)
267 {
268 // GCD(0,x) == GCD(x,0) == x
269 if (u == 0 || v == 0)
270 return u | v;
271
272 // Let shift := lg K, where K is the greatest power of 2
273 // dividing both u and v.
274 uint32_t shift = log2_lsb(u | v);
275
276 u >>= shift;
277 u >>= log2_lsb(u);
278
279 // From here on, u is always odd.
280 v >>= shift;
281 do {
282 v >>= log2_lsb(v);
283
284 // Now u and v are both odd. Swap if necessary so u <= v,
285 // then set v = v - u (which is even). For bignums, the
286 // swapping is just pointer movement, and the subtraction
287 // can be done in-place.
288 if (u > v) {
289 uint64_t t = u;
290 u = v;
291 v = t;
292 }
293 v = v - u;
294 } while (v != 0);
295
296 return u << shift;
297 }
298
299 template <class T>
300 inline T lcm(const T& u, const T& v)
301 {
302 (void) u;
303 (void) v;
304 //bool UNSUPPORTED_TYPE = false;
305 throw SpartaException("Unsupported type for lcm: ") << typeid(T).name();
306 }
307
308 template <>
309 inline uint32_t lcm<uint32_t>(const uint32_t &u, const uint32_t &v)
310 {
311 if (u == 1) {
312 return v;
313 } else if (v == 1) {
314 return u;
315 } else {
316 // Do it this way to avoid potential overflows from large numbers
317 return u / gcd(u, v) * v;
318 }
319 }
320
321 template <>
322 inline uint64_t lcm<uint64_t>(const uint64_t &u, const uint64_t &v)
323 {
324 if (u == 1) {
325 return v;
326 } else if (v == 1) {
327 return u;
328 } else {
329 // Do it this way to avoid potential overflows from large numbers
330 return u / gcd(u, v) * v;
331 }
332 }
333
334 template <class T>
335 inline T
336 safe_power(T n, T e)
337 {
338 static_assert(std::is_integral<T>::value, "sparta::safe_power only supports integer data types");
339
340 if (e == 0)
341 return 1;
342
343 T result = n;
344 for (T x = 1; x < e; x++)
345 {
346 T old_result = result;
347 result *= n;
348 if (old_result > result)
349 {
350 throw SpartaException("power() overflowed!");
351 }
352 }
353 return result;
354 }
355
359 template <typename T>
360 inline
361 typename std::enable_if<
362 std::is_floating_point<T>::value,
363 bool>::type
364 approximatelyEqual(const T a, const T b,
365 const T epsilon = std::numeric_limits<T>::epsilon())
366 {
367 const T fabs_a = std::fabs(a);
368 const T fabs_b = std::fabs(b);
369 const T fabs_diff = std::fabs(a - b);
370
371 return fabs_diff <= ((fabs_a < fabs_b ? fabs_b : fabs_a) * epsilon);
372 }
373
375 struct RandNumGen {
376 static std::mt19937 & get() {
377 static std::mt19937 rng(time(nullptr));
378 return rng;
379 }
380 };
381
383 template <typename T>
384 inline
385 typename std::enable_if<
386 std::is_integral<T>::value,
387 T>::type
388 chooseRand()
389 {
390 std::uniform_int_distribution<T> dist;
391 return dist(RandNumGen::get());
392 }
393
395 template <typename T>
396 inline
397 typename std::enable_if<
398 std::is_floating_point<T>::value,
399 T>::type
400 chooseRand()
401 {
402 std::normal_distribution<T> dist(0, 1000);
403 return dist(RandNumGen::get());
404 }
405
406 } // utils
407} // sparta
Set of macros for Sparta assertions. Caught by the framework.
Exception class for all of Sparta.
Macros for handling exponential backoff.
Static/global random number generator.