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