1 //
2 // Copyright 2013 Francisco Jerez
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22 
23 #ifndef CLOVER_UTIL_ALGORITHM_HPP
24 #define CLOVER_UTIL_ALGORITHM_HPP
25 
26 #include <algorithm>
27 #include <sstream>
28 #include <stdexcept>
29 
30 #include "util/range.hpp"
31 #include "util/functional.hpp"
32 
33 namespace clover {
34    namespace detail {
35       template<typename R>
36       using preferred_reference_type = decltype(*std::declval<R>().begin());
37    }
38 
39    ///
40    /// Return the first element in a range.
41    ///
42    template<typename R>
43    detail::preferred_reference_type<R>
head(R && r)44    head(R &&r) {
45       assert(!r.empty());
46       return r.front();
47    }
48 
49    ///
50    /// Return all elements in a range but the first.
51    ///
52    template<typename R>
53    slice_range<R>
tail(R && r)54    tail(R &&r) {
55       assert(!r.empty());
56       return { std::forward<R>(r), 1, r.size() };
57    }
58 
59    ///
60    /// Return the only element in a range.
61    ///
62    template<typename R>
63    detail::preferred_reference_type<R>
unique(R && r)64    unique(R &&r) {
65       if (r.size() != 1)
66          throw std::out_of_range("");
67 
68       return r.front();
69    }
70 
71    ///
72    /// Combine a variable number of ranges element-wise in a single
73    /// range of tuples.
74    ///
75    template<typename... Rs>
76    adaptor_range<zips, Rs...>
zip(Rs &&...rs)77    zip(Rs &&... rs) {
78       return map(zips(), std::forward<Rs>(rs)...);
79    }
80 
81    ///
82    /// Evaluate the elements of a range.
83    ///
84    /// Useful because most of the range algorithms evaluate their
85    /// result lazily.
86    ///
87    template<typename R>
88    void
eval(R && r)89    eval(R &&r) {
90       for (auto i = r.begin(), e = r.end(); i != e; ++i)
91          *i;
92    }
93 
94    ///
95    /// Apply functor \a f element-wise on a variable number of ranges
96    /// \a rs.
97    ///
98    /// The functor \a f should take as many arguments as ranges are
99    /// provided.
100    ///
101    template<typename F, typename... Rs>
102    void
for_each(F && f,Rs &&...rs)103    for_each(F &&f, Rs &&... rs) {
104       eval(map(std::forward<F>(f), std::forward<Rs>(rs)...));
105    }
106 
107    ///
108    /// Copy all elements from range \a r into an output container
109    /// starting from iterator \a i.
110    ///
111    template<typename R, typename I>
112    void
copy(R && r,I i)113    copy(R &&r, I i) {
114       for (detail::preferred_reference_type<R> x : r)
115          *(i++) = x;
116    }
117 
118    ///
119    /// Reduce the elements of range \a r by applying functor \a f
120    /// element by element.
121    ///
122    /// \a f should take an accumulator value (which is initialized to
123    /// \a a) and an element value as arguments, and return an updated
124    /// accumulator value.
125    ///
126    /// \returns The final value of the accumulator.
127    ///
128    template<typename F, typename A, typename R>
129    A
fold(F && f,A a,R && r)130    fold(F &&f, A a, R &&r) {
131       for (detail::preferred_reference_type<R> x : r)
132          a = f(a, x);
133 
134       return a;
135    }
136 
137    ///
138    /// Return how many elements of range \a r are equal to \a x.
139    ///
140    template<typename T, typename R>
141    typename std::remove_reference<R>::type::size_type
count(T && x,R && r)142    count(T &&x, R &&r) {
143       typename std::remove_reference<R>::type::size_type n = 0;
144 
145       for (detail::preferred_reference_type<R> y : r) {
146          if (x == y)
147             n++;
148       }
149 
150       return n;
151    }
152 
153    ///
154    /// Return the first element in range \a r for which predicate \a f
155    /// evaluates to true.
156    ///
157    template<typename F, typename R>
158    detail::preferred_reference_type<R>
find(F && f,R && r)159    find(F &&f, R &&r) {
160       for (detail::preferred_reference_type<R> x : r) {
161          if (f(x))
162             return x;
163       }
164 
165       throw std::out_of_range("");
166    }
167 
168    ///
169    /// Return true if the element-wise application of predicate \a f
170    /// on \a rs evaluates to true for all elements.
171    ///
172    template<typename F, typename... Rs>
173    bool
all_of(F && f,Rs &&...rs)174    all_of(F &&f, Rs &&... rs) {
175       for (auto b : map(f, rs...)) {
176          if (!b)
177             return false;
178       }
179 
180       return true;
181    }
182 
183    ///
184    /// Return true if the element-wise application of predicate \a f
185    /// on \a rs evaluates to true for any element.
186    ///
187    template<typename F, typename... Rs>
188    bool
any_of(F && f,Rs &&...rs)189    any_of(F &&f, Rs &&... rs) {
190       for (auto b : map(f, rs...)) {
191          if (b)
192             return true;
193       }
194 
195       return false;
196    }
197 
198    ///
199    /// Erase elements for which predicate \a f evaluates to true from
200    /// container \a r.
201    ///
202    template<typename F, typename R>
203    void
erase_if(F && f,R && r)204    erase_if(F &&f, R &&r) {
205       auto i = r.begin(), e = r.end();
206 
207       for (auto j = r.begin(); j != e; ++j) {
208          if (!f(*j)) {
209             if (j != i)
210                *i = std::move(*j);
211             ++i;
212          }
213       }
214 
215       r.erase(i, e);
216    }
217 
218    ///
219    /// Build a vector of string from a space separated string
220    /// quoted parts content is preserved and unquoted
221    ///
222    inline std::vector<std::string>
tokenize(const std::string & s)223    tokenize(const std::string &s) {
224       std::vector<std::string> ss;
225       std::ostringstream oss;
226 
227       // OpenCL programs can pass a quoted argument, most frequently the
228       // include path. This is useful so that path containing spaces is
229       // treated as a single argument instead of being split by the spaces.
230       // Additionally, the argument should also be unquoted before being
231       // passed to the compiler. We avoid using std::string::replace here to
232       // remove quotes, as the single and double quote characters can be a
233       // part of the file name.
234       bool escape_next = false;
235       bool in_quote_double = false;
236       bool in_quote_single = false;
237 
238       for (auto c : s) {
239          if (escape_next) {
240             oss.put(c);
241             escape_next = false;
242          } else if (c == '\\') {
243             escape_next = true;
244          } else if (c == '"' && !in_quote_single) {
245             in_quote_double = !in_quote_double;
246          } else if (c == '\'' && !in_quote_double) {
247             in_quote_single = !in_quote_single;
248          } else if (c != ' ' || in_quote_single || in_quote_double) {
249             oss.put(c);
250          } else if (oss.tellp() > 0) {
251             ss.emplace_back(oss.str());
252             oss.str("");
253          }
254       }
255 
256       if (oss.tellp() > 0)
257          ss.emplace_back(oss.str());
258 
259       if (in_quote_double || in_quote_single)
260          throw invalid_build_options_error();
261 
262       return ss;
263    }
264 
265    ///
266    /// Build a \a sep separated string from a vector of T
267    ///
268    template<typename T>
269    std::string
detokenize(const std::vector<T> & ss,const std::string & sep)270    detokenize(const std::vector<T> &ss, const std::string &sep) {
271       std::string r;
272 
273       for (const auto &s : ss)
274          r += (r.empty() ? "" : sep) + std::to_string(s);
275 
276       return r;
277    }
278 
279    ///
280    /// Build a \a sep separated string from a vector of string
281    ///
282    template <>
283    inline std::string
detokenize(const std::vector<std::string> & ss,const std::string & sep)284    detokenize(const std::vector<std::string> &ss, const std::string &sep) {
285       std::string r;
286 
287       for (const auto &s : ss)
288          r += (r.empty() || s.empty() ? "" : sep) + s;
289 
290       return r;
291    }
292 }
293 
294 #endif
295