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
14namespace sparta {
15 namespace utils {
16
17 inline double log2 (double x) {
18 // double y = std::log(x) / std::log(2.0);
19 // return y;
20 return std::log2(x);
21 }
22
23 template <class T>
24 inline uint32_t log2_lsb(const T& x)
25 {
26 (void) x;
27 //bool UNSUPPORTED_TYPE = false;
28 throw SpartaException("Unsupported type for log2_lsb: ") << typeid(T).name();
29 }
30
31 template <>
32 inline uint32_t log2_lsb<uint32_t>(const uint32_t &x)
33 {
34 // This uses a DeBrujin sequence to find the index
35 // (0..31) of the least significant bit in iclass
36 // Refer to Leiserson's bitscan algorithm:
37 // http://chessprogramming.wikispaces.com/BitScan
38 static const uint32_t index32[32] = {
39 0, 1, 28, 2, 29, 14, 24, 3,
40 30, 22, 20, 15, 25, 17, 4, 8,
41 31, 27, 13, 23, 21, 19, 16, 7,
42 26, 12, 18, 6, 11, 5, 10, 9
43 };
44
45 static const uint32_t debruijn32 = 0x077CB531u;
46
47 return index32[((x & -x) * debruijn32) >> 27];
48 }
49
50 template <>
51 inline uint32_t log2_lsb<uint64_t>(const uint64_t &x)
52 {
53 // This uses a DeBrujin sequence to find the index
54 // (0..63) of the least significant bit in iclass
55 // Refer to Leiserson's bitscan algorithm:
56 // http://chessprogramming.wikispaces.com/BitScan
57 static const uint32_t index64[64] = {
58 63, 0, 58, 1, 59, 47, 53, 2,
59 60, 39, 48, 27, 54, 33, 42, 3,
60 61, 51, 37, 40, 49, 18, 28, 20,
61 55, 30, 34, 11, 43, 14, 22, 4,
62 62, 57, 46, 52, 38, 26, 32, 41,
63 50, 36, 17, 19, 29, 10, 13, 21,
64 56, 45, 25, 31, 35, 16, 9, 12,
65 44, 24, 15, 8, 23, 7, 6, 5
66 };
67
68 static const uint64_t debruijn64 = 0x07EDD5E59A4E28C2ull;
69
70 return index64[((x & -x) * debruijn64) >> 58];
71 }
72
73 template <class T>
74 inline uint64_t floor_log2(T x)
75 {
76 // floor_log2(x) is the index of the most-significant 1 bit of x.
77 // (This is the old iterative version)
78 // NOTE: This function returns 0 for log2(0), but mathematically, it should be undefined
79 // throw SpartaException("floor_log2(0) is undefined");
80 uint64_t y = 0;
81 while (x >>= 1) {
82 y++;
83 }
84 return y;
85 }
86
87 template<>
88 inline uint64_t floor_log2<double>(double x)
89 {
90 return std::floor(log2(x));
91 }
92
93 template <>
94 inline uint64_t floor_log2<uint32_t>(uint32_t x)
95 {
96 if (x == 0) {
97 // NOTE: This function returns 0 for log2(0) for compatibility with the old version,
98 // but mathematically, it should be undefined
99 // throw SpartaException("floor_log2(0) is undefined");
100 return 0;
101 }
102
103 // This is a fast floor(log2(x)) based on DeBrujin's algorithm
104 // (based on generally available and numerous sources)
105 static const uint64_t lut[] = {
106 0, 9, 1, 10, 13, 21, 2, 29,
107 11, 14, 16, 18, 22, 25, 3, 30,
108 8, 12, 20, 28, 15, 17, 24, 7,
109 19, 27, 23, 6, 26, 5, 4, 31};
110
111 x |= x >> 1;
112 x |= x >> 2;
113 x |= x >> 4;
114 x |= x >> 8;
115 x |= x >> 16;
116 return lut[(uint32_t)(x * 0x07C4ACDDul) >> 27];
117 }
118
119 template <>
120 inline uint64_t floor_log2<uint64_t>(uint64_t x)
121 {
122 if (x == 0) {
123 // NOTE: This function returns 0 for log2(0) for compatibility with the old version,
124 // but mathematically, it should be undefined
125 // throw SpartaException("floor_log2(0) is undefined");
126 return 0;
127 }
128
129 // This is a fast floor(log2(x)) based on DeBrujin's algorithm
130 // (based on generally available and numerous sources)
131 static const uint64_t lut[] = {
132 63, 0, 58, 1, 59, 47, 53, 2,
133 60, 39, 48, 27, 54, 33, 42, 3,
134 61, 51, 37, 40, 49, 18, 28, 20,
135 55, 30, 34, 11, 43, 14, 22, 4,
136 62, 57, 46, 52, 38, 26, 32, 41,
137 50, 36, 17, 19, 29, 10, 13, 21,
138 56, 45, 25, 31, 35, 16, 9, 12,
139 44, 24, 15, 8, 23, 7, 6, 5};
140
141 x |= x >> 1;
142 x |= x >> 2;
143 x |= x >> 4;
144 x |= x >> 8;
145 x |= x >> 16;
146 x |= x >> 32;
147 return lut[((uint64_t)((x - (x >> 1)) * 0x07EDD5E59A4E28C2ull)) >> 58];
148 }
149
150 inline uint64_t ceil_log2 (uint64_t x)
151 {
152 // If x is a power of 2 then ceil_log2(x) is floor_log2(x).
153 // Otherwise ceil_log2(x) is floor_log2(x) + 1.
154 uint64_t y = floor_log2(x);
155 if ((static_cast<uint64_t>(1) << y) != x) {
156 y++;
157 }
158 return y;
159 }
160
161 inline uint64_t pow2 (uint64_t x) {
162 uint64_t y = static_cast<uint64_t>(1) << x;
163 return y;
164 }
165
166 inline bool is_power_of_2 (uint64_t x) {
167 bool y = x && !(x & (x - 1));
168 return y;
169 }
170
171 inline uint64_t next_power_of_2(uint64_t v)
172 {
173 if(v < 2) {
174 return 1ull;
175 }
176 return 1ull << ((sizeof(uint64_t) * 8) - __builtin_clzll(v - 1ull));
177 }
178
179 inline uint64_t ones (uint64_t x) {
180 uint64_t y = (static_cast<uint64_t>(1) << x) - 1;
181 return y;
182 }
183
184 // Be careful of the types, here. The default
185 // version of abs() expects to return the same type
186 // as the given argument x. This is ok for float, double, eg.
187 // but not for unsigned integer types, since the x < 0 test
188 // will always be false.
189 template <class T>
190 inline T abs(T x)
191 {
192 return (x < 0 ? -x : x);
193 }
194
195 template <>
196 inline uint8_t abs<uint8_t>(uint8_t x) {
197 uint8_t sign_mask = int8_t(x) >> 7;
198 return (x + sign_mask) ^ sign_mask;
199 }
200
201 template <>
202 inline uint16_t abs<uint16_t>(uint16_t x) {
203 uint16_t sign_mask = int16_t(x) >> 15;
204 return (x + sign_mask) ^ sign_mask;
205 }
206
207 template <>
208 inline uint32_t abs<uint32_t>(uint32_t x) {
209 uint32_t sign_mask = int32_t(x) >> 31;
210 return (x + sign_mask) ^ sign_mask;
211 }
212
213 template <>
214 inline uint64_t abs<uint64_t>(uint64_t x) {
215 uint64_t sign_mask = int64_t(x) >> 63;
216 return (x + sign_mask) ^ sign_mask;
217 }
218
219 template <class T>
220 inline T gcd(T u, T v)
221 {
222 (void) u;
223 (void) v;
224 static_assert("This is an unsupported type");
225 }
226
227 // Adapted from WIKI article on binary GCD algorithm
228 template <>
229 inline uint32_t gcd<uint32_t>(uint32_t u, uint32_t v)
230 {
231 // GCD(0,x) == GCD(x,0) == x
232 if (u == 0 || v == 0)
233 return u | v;
234
235 // Let shift := lg K, where K is the greatest power of 2
236 // dividing both u and v.
237 uint32_t shift = log2_lsb(u | v);
238
239 u >>= shift;
240 u >>= log2_lsb(u);
241
242 // From here on, u is always odd.
243 v >>= shift;
244 do {
245 v >>= log2_lsb(v);
246
247 // Now u and v are both odd. Swap if necessary so u <= v,
248 // then set v = v - u (which is even). For bignums, the
249 // swapping is just pointer movement, and the subtraction
250 // can be done in-place.
251 if (u > v) {
252 uint32_t t = u;
253 u = v;
254 v = t;
255 }
256 v = v - u;
257 } while (v != 0);
258
259 return u << shift;
260 }
261
262 // Adapted from WIKI article on binary GCD algorithm
263 template <>
264 inline uint64_t gcd<uint64_t>(uint64_t u, uint64_t v)
265 {
266 // GCD(0,x) == GCD(x,0) == x
267 if (u == 0 || v == 0)
268 return u | v;
269
270 // Let shift := lg K, where K is the greatest power of 2
271 // dividing both u and v.
272 uint32_t shift = log2_lsb(u | v);
273
274 u >>= shift;
275 u >>= log2_lsb(u);
276
277 // From here on, u is always odd.
278 v >>= shift;
279 do {
280 v >>= log2_lsb(v);
281
282 // Now u and v are both odd. Swap if necessary so u <= v,
283 // then set v = v - u (which is even). For bignums, the
284 // swapping is just pointer movement, and the subtraction
285 // can be done in-place.
286 if (u > v) {
287 uint64_t t = u;
288 u = v;
289 v = t;
290 }
291 v = v - u;
292 } while (v != 0);
293
294 return u << shift;
295 }
296
297 template <class T>
298 inline T lcm(const T& u, const T& v)
299 {
300 (void) u;
301 (void) v;
302 //bool UNSUPPORTED_TYPE = false;
303 throw SpartaException("Unsupported type for lcm: ") << typeid(T).name();
304 }
305
306 template <>
307 inline 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 inline 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 inline 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
354 } // utils
355} // sparta
Set of macros for Sparta assertions. Caught by the framework.
Exception class for all of Sparta.
Macros for handling exponential backoff.