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 &param) {
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