1 #ifndef AMGCL_UTIL_HPP
2 #define AMGCL_UTIL_HPP
3
4 /*
5 The MIT License
6
7 Copyright (c) 2012-2021 Denis Demidov <dennis.demidov@gmail.com>
8
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 */
27
28 /**
29 * \file amgcl/util.hpp
30 * \author Denis Demidov <dennis.demidov@gmail.com>
31 * \brief Various utilities.
32 */
33
34 #include <iostream>
35 #include <iomanip>
36 #include <iterator>
37 #include <vector>
38 #include <array>
39 #include <string>
40 #include <set>
41 #include <complex>
42 #include <limits>
43 #include <stdexcept>
44 #include <cstddef>
45
46 // If asked explicitly, or if boost is available, enable
47 // using boost::propert_tree::ptree as amgcl parameters:
48 #ifndef AMGCL_NO_BOOST
49 # include <boost/property_tree/ptree.hpp>
50 #endif
51
52 #include <amgcl/io/ios_saver.hpp>
53
54 /* Performance measurement macros
55 *
56 * If AMGCL_PROFILING macro is defined at compilation, then AMGCL_TIC(name) and
57 * AMGCL_TOC(name) macros correspond to prof.tic(name) and prof.toc(name).
58 * amgcl::prof should be an instance of amgcl::profiler<> defined in a user
59 * code similar to:
60 * \code
61 * namespace amgcl { profiler<> prof; }
62 * \endcode
63 * If AMGCL_PROFILING is undefined, then AMGCL_TIC and AMGCL_TOC are noop macros.
64 */
65 #ifdef AMGCL_PROFILING
66 # if !defined(AMGCL_TIC) || !defined(AMGCL_TOC)
67 # include <amgcl/profiler.hpp>
68 # define AMGCL_TIC(name) amgcl::prof.tic(name);
69 # define AMGCL_TOC(name) amgcl::prof.toc(name);
70 namespace amgcl { extern profiler<> prof; }
71 # endif
72 #else
73 # ifndef AMGCL_TIC
74 # define AMGCL_TIC(name)
75 # endif
76 # ifndef AMGCL_TOC
77 # define AMGCL_TOC(name)
78 # endif
79 #endif
80
81 #define AMGCL_DEBUG_SHOW(x) \
82 std::cout << std::setw(20) << #x << ": " \
83 << std::setw(15) << std::setprecision(8) << std::scientific \
84 << (x) << std::endl
85
86 namespace amgcl {
87
88 /// Throws \p message if \p condition is not true.
89 template <class Condition, class Message>
precondition(const Condition & condition,const Message & message)90 void precondition(const Condition &condition, const Message &message) {
91 #ifdef _MSC_VER
92 # pragma warning(push)
93 # pragma warning(disable: 4800)
94 #endif
95 if (!condition) throw std::runtime_error(message);
96 #ifdef _MSC_VER
97 # pragma warning(pop)
98 #endif
99 }
100
101 #ifndef AMGCL_NO_BOOST
102
103 #define AMGCL_PARAMS_IMPORT_VALUE(p, name) \
104 name( p.get(#name, params().name) )
105
106 #define AMGCL_PARAMS_IMPORT_CHILD(p, name) \
107 name( p.get_child(#name, amgcl::detail::empty_ptree()) )
108
109 #define AMGCL_PARAMS_EXPORT_VALUE(p, path, name) \
110 p.put(std::string(path) + #name, name)
111
112 namespace detail {
113
114 template <typename T>
params_export_child(boost::property_tree::ptree & p,const std::string & path,const char * name,const T & obj)115 inline void params_export_child(
116 boost::property_tree::ptree &p,
117 const std::string &path,
118 const char *name, const T &obj)
119 {
120 obj.get(p, std::string(path) + name + ".");
121 }
122
123 template <>
params_export_child(boost::property_tree::ptree & p,const std::string & path,const char * name,const boost::property_tree::ptree & obj)124 inline void params_export_child(
125 boost::property_tree::ptree &p,
126 const std::string &path, const char *name,
127 const boost::property_tree::ptree &obj)
128 {
129 p.add_child(std::string(path) + name, obj);
130 }
131
132 } // namespace detail
133
134 #define AMGCL_PARAMS_EXPORT_CHILD(p, path, name) \
135 amgcl::detail::params_export_child(p, path, #name, name)
136
137 // Missing parameter action
138 #ifndef AMGCL_PARAM_MISSING
139 # define AMGCL_PARAM_MISSING(name) (void)0
140 #endif
141
142 // Unknown parameter action
143 #ifndef AMGCL_PARAM_UNKNOWN
144 # define AMGCL_PARAM_UNKNOWN(name) \
145 std::cerr << "AMGCL WARNING: unknown parameter " << name << std::endl
146 #endif
147
check_params(const boost::property_tree::ptree & p,const std::set<std::string> & names)148 inline void check_params(
149 const boost::property_tree::ptree &p,
150 const std::set<std::string> &names
151 )
152 {
153 for(const auto &n : names) {
154 if (!p.count(n)) {
155 AMGCL_PARAM_MISSING(n);
156 }
157 }
158 for(const auto &v : p) {
159 if (!names.count(v.first)) {
160 AMGCL_PARAM_UNKNOWN(v.first);
161 }
162 }
163 }
164
check_params(const boost::property_tree::ptree & p,const std::set<std::string> & names,const std::set<std::string> & opt_names)165 inline void check_params(
166 const boost::property_tree::ptree &p,
167 const std::set<std::string> &names,
168 const std::set<std::string> &opt_names
169 )
170 {
171 for(const auto &n : names) {
172 if (!p.count(n)) {
173 AMGCL_PARAM_MISSING(n);
174 }
175 }
176 for(const auto &n : opt_names) {
177 if (!p.count(n)) {
178 AMGCL_PARAM_MISSING(n);
179 }
180 }
181 for(const auto &v : p) {
182 if (!names.count(v.first) && !opt_names.count(v.first)) {
183 AMGCL_PARAM_UNKNOWN(v.first);
184 }
185 }
186 }
187
188 // Put parameter in form "key=value" into a boost::property_tree::ptree
put(boost::property_tree::ptree & p,const std::string & param)189 inline void put(boost::property_tree::ptree &p, const std::string ¶m) {
190 size_t eq_pos = param.find('=');
191 if (eq_pos == std::string::npos)
192 throw std::invalid_argument("param in amgcl::put() should have \"key=value\" format!");
193 p.put(param.substr(0, eq_pos), param.substr(eq_pos + 1));
194 }
195
196 #endif
197
198 namespace detail {
199
200 #ifndef AMGCL_NO_BOOST
empty_ptree()201 inline const boost::property_tree::ptree& empty_ptree() {
202 static const boost::property_tree::ptree p;
203 return p;
204 }
205 #endif
206
207 struct empty_params {
empty_paramsamgcl::detail::empty_params208 empty_params() {}
209
210 #ifndef AMGCL_NO_BOOST
empty_paramsamgcl::detail::empty_params211 empty_params(const boost::property_tree::ptree &p) {
212 for(const auto &v : p) {
213 AMGCL_PARAM_UNKNOWN(v.first);
214 }
215 }
getamgcl::detail::empty_params216 void get(boost::property_tree::ptree&, const std::string&) const {}
217 #endif
218 };
219
220 } // namespace detail
221
222 // Iterator range
223 template <class Iterator>
224 class iterator_range {
225 public:
226 typedef Iterator iterator;
227 typedef Iterator const_iterator;
228 typedef typename std::iterator_traits<Iterator>::value_type value_type;
229
iterator_range(Iterator b,Iterator e)230 iterator_range(Iterator b, Iterator e)
231 : b(b), e(e) {}
232
size() const233 ptrdiff_t size() const {
234 return std::distance(b, e);
235 }
236
begin() const237 Iterator begin() const {
238 return b;
239 }
240
end() const241 Iterator end() const {
242 return e;
243 }
244
operator [](size_t i) const245 const value_type& operator[](size_t i) const {
246 return b[i];
247 }
248
operator [](size_t i)249 value_type& operator[](size_t i) {
250 return b[i];
251 }
252 private:
253 Iterator b, e;
254 };
255
256 template <class Iterator>
make_iterator_range(Iterator b,Iterator e)257 iterator_range<Iterator> make_iterator_range(Iterator b, Iterator e) {
258 return iterator_range<Iterator>(b, e);
259 }
260
261 // N-dimensional dense matrix
262 template <class T, int N>
263 class multi_array {
264 static_assert(N > 0, "Wrong number of dimensions");
265
266 public:
267 template <class... I>
multi_array(I...n)268 multi_array(I... n) {
269 static_assert(sizeof...(I) == N, "Wrong number of dimensions");
270 buf.resize(init(n...));
271 }
272
size() const273 size_t size() const {
274 return buf.size();
275 }
276
stride(int i) const277 int stride(int i) const {
278 return strides[i];
279 }
280
281 template <class... I>
operator ()(I...i) const282 T operator()(I... i) const {
283 static_assert(sizeof...(I) == N, "Wrong number of indices");
284 return buf[index(i...)];
285 }
286
287 template <class... I>
operator ()(I...i)288 T& operator()(I... i) {
289 static_assert(sizeof...(I) == N, "Wrong number of indices");
290 return buf[index(i...)];
291 }
292
data() const293 const T* data() const {
294 return buf.data();
295 }
296
data()297 T* data() {
298 return buf.data();
299 }
300 private:
301 std::array<int, N> strides;
302 std::vector<T> buf;
303
304 template <class... I>
index(int i,I...tail) const305 int index(int i, I... tail) const {
306 return strides[N - sizeof...(I) - 1] * i + index(tail...);
307 }
308
index(int i) const309 int index(int i) const {
310 return strides[N-1] * i;
311 }
312
313 template <class... I>
init(int i,I...tail)314 int init(int i, I... tail) {
315 int size = init(tail...);
316 strides[N - sizeof...(I) - 1] = size;
317 return i * size;
318 }
319
init(int i)320 int init(int i) {
321 strides[N-1] = 1;
322 return i;
323 }
324 };
325
326 template <class T>
327 class circular_buffer {
328 public:
circular_buffer(size_t n)329 circular_buffer(size_t n) : start(0) {
330 buf.reserve(n);
331 }
332
size() const333 size_t size() const {
334 return buf.size();
335 }
336
push_back(const T & v)337 void push_back(const T &v) {
338 if (buf.size() < buf.capacity()) {
339 buf.push_back(v);
340 } else {
341 buf[start] = v;
342 start = (start + 1) % buf.capacity();
343 }
344 }
345
operator [](size_t i) const346 const T& operator[](size_t i) const {
347 return buf[(start + i) % buf.capacity()];
348 }
349
operator [](size_t i)350 T& operator[](size_t i) {
351 return buf[(start + i) % buf.capacity()];
352 }
353
clear()354 void clear() {
355 buf.clear();
356 start = 0;
357 }
358
359 private:
360 size_t start;
361 std::vector<T> buf;
362 };
363
364
365 namespace detail {
366
367 template <class T>
eps(size_t n)368 T eps(size_t n) {
369 return 2 * std::numeric_limits<T>::epsilon() * n;
370 }
371
372 } // namespace detail
373
374 template <class T> struct is_complex : std::false_type {};
375 template <class T> struct is_complex< std::complex<T> > : std::true_type {};
376
human_readable_memory(size_t bytes)377 inline std::string human_readable_memory(size_t bytes) {
378 static const char *suffix[] = {"B", "K", "M", "G", "T"};
379
380 int i = 0;
381 double m = static_cast<double>(bytes);
382 for(; i < 4 && m >= 1024.0; ++i, m /= 1024.0);
383
384 std::ostringstream s;
385 s << std::fixed << std::setprecision(2) << m << " " << suffix[i];
386 return s.str();
387 }
388
389 namespace detail {
390
391 class non_copyable {
392 protected:
393 non_copyable() = default;
394 ~non_copyable() = default;
395
396 non_copyable(non_copyable const &) = delete;
397 void operator=(non_copyable const &x) = delete;
398 };
399
400 } // namespace detail
401
402 namespace error {
403
404 struct empty_level {};
405
406 } // namespace error
407 } // namespace amgcl
408
409 namespace std {
410
411 // Read pointers from input streams.
412 // This allows to exchange pointers through boost::property_tree::ptree.
413 template <class T>
operator >>(istream & is,T * & ptr)414 inline istream& operator>>(istream &is, T* &ptr) {
415 amgcl::ios_saver ss(is);
416
417 size_t val;
418 is >> std::hex >> val;
419
420 ptr = reinterpret_cast<T*>(val);
421
422 return is;
423 }
424
425 } // namespace std
426
427
428 #endif
429