Algorithms Library Toolkit
A toolkit for algorithms, especially for algorithms on formal languages
dry-comparisons.hpp
Go to the documentation of this file.
1
6/*
7 * This is free and unencumbered software released into the public domain.
8 *
9 * Anyone is free to copy, modify, publish, use, compile, sell, or
10 * distribute this software, either in source code form or as a compiled
11 * binary, for any purpose, commercial or non-commercial, and by any
12 * means.
13 *
14 * In jurisdictions that recognize copyright laws, the author or authors
15 * of this software dedicate any and all copyright interest in the
16 * software to the public domain. We make this dedication for the benefit
17 * of the public at large and to the detriment of our heirs and
18 * successors. We intend this dedication to be an overt act of
19 * relinquishment in perpetuity of all present and future rights to this
20 * software under copyright law.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 *
30 * For more information, please refer to <http://unlicense.org>
31 *
32 * Original source https://github.com/rollbear/dry-comparisons
33 *
34 * @author Björn Fahller
35 * @modified Jan Trávníček
36 */
37
38#pragma once
39
40#include <utility>
41#include <type_traits>
42#include <tuple>
43#include <functional>
44#include <iosfwd>
45
46namespace ext {
47
48namespace internal {
49
50template <typename, typename = void>
51struct printable;
52template <typename ... Ts>
53struct printable<std::tuple<Ts...>, std::void_t<decltype(std::declval<std::ostream&>() << std::declval<const Ts&>())...>>
54{
55 using type = void;
56};
57
58template <typename T>
59using printable_t = typename printable<T>::type;
60
61template <typename F, typename ... Args>
62struct bound
63{
64 using RT = std::invoke_result_t<F, Args...>;
65
66 template <typename F_, typename ... As>
67 constexpr bound(F_&& f_, As&& ... as) : f(std::forward<F_>(f_)), args(std::forward<As>(as)...) {}
68
69 constexpr operator RT() const
70 noexcept(std::is_nothrow_invocable_v<const F&, const Args&...>)
71 {
72 return std::apply(f, args);
73 }
74
75 F f;
76 std::tuple<Args...> args;
77};
78
79template <typename ... Ts>
80struct binder
81{
82 std::tuple<Ts...> values;
83 template <typename T, typename F>
84 constexpr bound<T, Ts...> bind(const F& f) const
85 {
86 return std::apply([&](auto&& ... vs){return bound<T, Ts...>{f, vs...};}, values);
87 }
88};
89
90class logical_tuple_base { };
91
92template <typename ... Ts>
93class logical_tuple : std::tuple<Ts...>, public logical_tuple_base
94{
95 using tuple = std::tuple<Ts...>;
96 constexpr const tuple& self() const { return *this; }
97protected:
98 using tuple::tuple;
99 template <typename F>
100 constexpr auto or_all(F&& f) const
101 {
102 return std::apply([&](const auto& ... v) { return (f(v) || ...);}, self());
103 }
104 template <typename F>
105 constexpr auto and_all(F&& f) const
106 {
107 return std::apply([&](const auto& ... v) { return (f(v) && ...);}, self());
108 }
109 template <typename RT, typename ... Args>
110 constexpr RT bind(Args&& ... args) const {
111 return std::apply([&](auto&&... f) {
112 binder<Args...> b{{args...}};
113 return RT{b.template bind<Ts>(std::forward<decltype(f)>(f))...}; }, self());
114 }
115 template <typename Char, typename Traits>
116 std::basic_ostream<Char, Traits>& print(const Char* label, std::basic_ostream<Char, Traits>& os) const
117 {
118 os << label << '{';
119 std::apply([&os](const auto& ... v) {
120 int first = 1;
121 ((os << &","[std::exchange(first, 0)] << v),...);
122 }, self());
123 return os << '}';
124 }
125};
126}
127
128
129template <typename ... T>
130class any_of : internal::logical_tuple<T...>
131{
132 using internal::logical_tuple<T...>::or_all;
133 using internal::logical_tuple<T...>::and_all;
134public:
135 using internal::logical_tuple<T...>::logical_tuple;
136
137 template <typename U>
138 constexpr auto operator==(const U& u) const
139 noexcept(noexcept(((std::declval<const T&>() == u) || ...)))
140 -> decltype(((std::declval<const T&>() == u) || ...))
141 {
142 return or_all([&](auto&& v) { return v == u;});
143 }
144
145 template <typename U>
146 constexpr auto operator!=(const U& u) const
147 noexcept(noexcept(((std::declval<const T&>() != u) && ...)))
148 -> decltype(((std::declval<const T&>() != u) && ...))
149 {
150 return and_all([&](auto v) { return v != u;});
151 }
152
153 template <typename U>
154 constexpr auto operator<(const U& u) const
155 noexcept(noexcept(((std::declval<const T&>() < u) || ...)))
156 -> decltype(((std::declval<const T&>() < u) || ...))
157 {
158 return or_all([&](auto&& v){ return v < u;});
159 }
160
161 template <typename U>
162 constexpr auto operator<=(const U& u) const
163 noexcept(noexcept(((std::declval<const T&>() <= u) || ...)))
164 -> decltype(((std::declval<const T&>() <= u) || ...))
165 {
166 return or_all([&](auto&& v){ return v <= u;});
167 }
168
169 template <typename U>
170 constexpr auto operator>(const U& u) const
171 noexcept(noexcept(((std::declval<const T&>() > u) || ...)))
172 -> decltype(((std::declval<const T&>() > u) || ...))
173 {
174 return or_all([&](auto&& v) { return v > u;});
175 }
176
177 template <typename U>
178 constexpr auto operator>=(const U& u) const
179 noexcept(noexcept(((std::declval<const T&>() >= u) || ...)))
180 -> decltype(((std::declval<const T&>() >= u) || ...))
181 {
182 return or_all([&](auto&& v) { return v >= u;});
183 }
184
185 template <typename V = std::tuple<T...>, typename = internal::printable_t<V>>
186 friend std::ostream& operator<<(std::ostream& os, const any_of& self)
187 {
188 return self.print("any_of", os);
189 }
190
191 constexpr explicit operator bool() const
192 noexcept(noexcept((std::declval<const T&>() || ...)))
193 {
194 return or_all([](auto&& v) { return v;});
195 }
196
197 template <typename ... Ts>
198 requires ( std::conjunction_v<std::is_copy_constructible<Ts>...>&& std::conjunction_v<std::is_invocable<T, Ts...>...> )
199 constexpr auto operator()(Ts&& ... ts) const
200 noexcept(std::conjunction_v<std::is_nothrow_move_constructible<T>...> && std::conjunction_v<std::is_nothrow_copy_constructible<Ts>...>)
201 -> any_of<internal::bound<T, Ts...>...>
202 {
203 using RT = any_of<internal::bound<T, Ts...>...>;
204 return this->template bind<RT>(std::forward<Ts>(ts)...);
205 }
206};
207
208template <typename ... T>
209class none_of : internal::logical_tuple<T...>
210{
211 using internal::logical_tuple<T...>::or_all;
212 using internal::logical_tuple<T...>::and_all;
213public:
214 using internal::logical_tuple<T...>::logical_tuple;
215
216 template <typename U>
217 constexpr auto operator==(const U& u) const
218 noexcept(noexcept(!((std::declval<const T&>() == u) || ...)))
219 -> decltype(!((std::declval<const T&>() == u) || ...))
220 {
221 return !or_all([&](auto&& v) { return v == u;});
222 }
223
224 template <typename U>
225 constexpr auto operator!=(const U& u) const
226 noexcept(noexcept(!((std::declval<const T&>() != u) && ...)))
227 -> decltype(!((std::declval<const T&>() != u) && ...))
228 {
229 return !and_all([&](auto && v){return v != u;});
230 }
231
232 template <typename U>
233 constexpr auto operator<(const U& u) const
234 noexcept(noexcept(!((std::declval<const T&>() < u) || ...)))
235 -> decltype(!((std::declval<const T&>() < u) || ...))
236 {
237 return !or_all([&](auto&& v){ return v < u;});
238 }
239
240 template <typename U>
241 constexpr auto operator<=(const U& u) const
242 noexcept(noexcept(!((std::declval<const T&>() <= u) || ...)))
243 -> decltype(!((std::declval<const T&>() <= u) || ...))
244 {
245 return !or_all([&](auto&& v){ return v <= u;});
246 }
247
248 template <typename U>
249 constexpr auto operator>(const U& u) const
250 noexcept(noexcept(!((std::declval<const T&>() > u) || ...)))
251 -> decltype(!((std::declval<const T&>() > u) || ...))
252 {
253 return !or_all([&](auto&& v) { return v > u;});
254 }
255
256 template <typename U>
257 constexpr auto operator>=(const U& u) const
258 noexcept(noexcept(!((std::declval<const T&>() >= u) || ...)))
259 -> decltype(!((std::declval<const T&>() >= u) || ...))
260 {
261 return !or_all([&](auto&& v){ return v >= u;});
262 }
263
264 template <typename V = std::tuple<T...>, typename = internal::printable_t<V>>
265 friend std::ostream& operator<<(std::ostream& os, const none_of& self)
266 {
267 return self.print("none_of", os);
268 }
269
270 constexpr explicit operator bool() const
271 noexcept(noexcept(!(std::declval<const T&>() || ...)))
272 {
273 return !or_all([](auto&& v) { return v;});
274 }
275
276 template <typename ... Ts>
277 requires ( std::conjunction_v<std::is_copy_constructible<Ts>...>&&std::conjunction_v<std::is_invocable<T, Ts...>...> )
278 constexpr auto operator()(Ts&& ... ts) const
279 noexcept( std::conjunction_v<std::is_nothrow_move_constructible<T>...> && std::conjunction_v<std::is_nothrow_copy_constructible<Ts>...>)
280 -> none_of<internal::bound<T, Ts...>...>
281 {
282 using RT = none_of<internal::bound<T, Ts...>...>;
283 return this->template bind<RT>(std::forward<Ts>(ts)...);
284 }
285};
286
287template <typename ... T>
289{
292public:
293 using internal::logical_tuple<T...>::logical_tuple;
294
295 template <typename U>
296 constexpr auto operator==(const U& u) const
297 noexcept(noexcept(((std::declval<const T&>() == u) && ...)))
298 -> decltype(((std::declval<const T&>() == u) && ...))
299 {
300 return and_all([&](auto&& v){ return v == u;});
301 }
302
303 template <typename U>
304 constexpr auto operator!=(const U& u) const
305 noexcept(noexcept(((std::declval<const T&>() != u) || ...)))
306 -> decltype(((std::declval<const T&>() != u) || ...))
307 {
308 return or_all([&](auto&& v){return v != u;});
309 }
310
311 template <typename U>
312 constexpr auto operator<(const U& u) const
313 noexcept(noexcept(((std::declval<const T&>() < u) && ...)))
314 -> decltype(((std::declval<const T&>() < u) && ...))
315 {
316 return and_all([&](auto&& v){ return v < u;});
317 }
318
319 template <typename U>
320 constexpr auto operator<=(const U& u) const
321 noexcept(noexcept(((std::declval<const T&>() <= u) && ...)))
322 -> decltype(((std::declval<const T&>() <= u) && ...))
323 {
324 return and_all([&](auto&& v){ return v <= u;});
325 }
326
327 template <typename U>
328 constexpr auto operator>(const U& u) const
329 noexcept(noexcept(((std::declval<const T&>() > u) && ...)))
330 -> decltype(((std::declval<const T&>() > u) && ...))
331 {
332 return and_all([&](auto&& v){ return v > u;});
333 }
334
335 template <typename U>
336 constexpr auto operator>=(const U& u) const
337 noexcept(noexcept(((std::declval<const T&>() >= u) && ...)))
338 -> decltype(((std::declval<const T&>() >= u) && ...))
339 {
340 return and_all([&](auto&& v){ return v >= u;});
341 }
342
343 template <typename V = std::tuple<T...>, typename = internal::printable_t<V>>
344 friend std::ostream& operator<<(std::ostream& os, const all_of& self)
345 {
346 return self.print("all_of", os);
347 }
348
349 constexpr explicit operator bool() const
350 noexcept(noexcept((std::declval<const T&>() && ...)))
351 {
352 return and_all([](auto&& v) -> bool { return v;});
353 }
354
355 template <typename ... Ts>
356 requires ( std::conjunction_v<std::is_copy_constructible<Ts>...>&& std::conjunction_v<std::is_invocable<T, Ts...>...> )
357 constexpr auto operator()(Ts&& ... ts) const
358 noexcept( std::conjunction_v<std::is_nothrow_move_constructible<T>...> && std::conjunction_v<std::is_nothrow_copy_constructible<Ts>...>)
359 -> all_of<internal::bound<T, Ts...>...>
360 {
361 using RT = all_of<internal::bound<T, Ts...>...>;
362 return this->template bind<RT>(std::forward<Ts>(ts)...);
363 }
364};
365
366template <typename U, typename T>
367requires ( std::is_base_of_v<internal::logical_tuple_base, T> && ! std::is_base_of_v < internal::logical_tuple_base, U > )
368constexpr auto operator==(const U& u, const T& a)
369noexcept(noexcept(a == u))
370{
371 return a == u;
372}
373
374template <typename U, typename T >
375requires ( std::is_base_of_v<internal::logical_tuple_base, T> && ! std::is_base_of_v < internal::logical_tuple_base, U > )
376constexpr auto operator!=(const U& u, const T& a)
377noexcept(noexcept(a != u))
378{
379 return a != u;
380}
381
382template <typename U, typename T >
383requires ( std::is_base_of_v<internal::logical_tuple_base, T> && ! std::is_base_of_v < internal::logical_tuple_base, U > )
384constexpr auto operator>(const U& u, const T& a)
385noexcept(noexcept(a < u))
386{
387 return a.operator < ( u );
388}
389
390template <typename U, typename T >
391requires ( std::is_base_of_v<internal::logical_tuple_base, T> && ! std::is_base_of_v < internal::logical_tuple_base, U > )
392constexpr auto operator>=(const U& u, const T& a)
393noexcept(noexcept(a <= u))
394{
395 return a <= u;
396}
397
398template <typename U, typename T >
399requires ( std::is_base_of_v<internal::logical_tuple_base, T> && ! std::is_base_of_v < internal::logical_tuple_base, U > )
400constexpr auto operator<(const U& u, const T& a)
401noexcept(noexcept(a > u))
402{
403 return a.operator > ( u );
404}
405
406template <typename U, typename T >
407requires ( std::is_base_of_v<internal::logical_tuple_base, T> && ! std::is_base_of_v < internal::logical_tuple_base, U > )
408constexpr auto operator<=(const U& u, const T& a)
409noexcept(noexcept(a >= u))
410{
411 return a >= u;
412}
413
414template <typename ... T>
415any_of(T&& ...) -> any_of<T...>;
416template <typename ... T>
417none_of(T&& ...) -> none_of<T...>;
418template <typename ... T>
419all_of(T&& ...) -> all_of<T...>;
420
421}
422
Definition: dry-comparisons.hpp:289
constexpr auto operator==(const U &u) const noexcept(noexcept(((std::declval< const T & >()==u) &&...))) -> decltype(((std::declval< const T & >()==u) &&...))
Definition: dry-comparisons.hpp:296
constexpr auto operator>=(const U &u) const noexcept(noexcept(((std::declval< const T & >() >=u) &&...))) -> decltype(((std::declval< const T & >() >=u) &&...))
Definition: dry-comparisons.hpp:336
constexpr auto operator>(const U &u) const noexcept(noexcept(((std::declval< const T & >() > u) &&...))) -> decltype(((std::declval< const T & >() > u) &&...))
Definition: dry-comparisons.hpp:328
friend std::ostream & operator<<(std::ostream &os, const all_of &self)
Definition: dry-comparisons.hpp:344
constexpr auto operator<(const U &u) const noexcept(noexcept(((std::declval< const T & >()< u) &&...))) -> decltype(((std::declval< const T & >()< u) &&...))
Definition: dry-comparisons.hpp:312
constexpr auto operator!=(const U &u) const noexcept(noexcept(((std::declval< const T & >() !=u)||...))) -> decltype(((std::declval< const T & >() !=u)||...))
Definition: dry-comparisons.hpp:304
constexpr auto operator<=(const U &u) const noexcept(noexcept(((std::declval< const T & >()<=u) &&...))) -> decltype(((std::declval< const T & >()<=u) &&...))
Definition: dry-comparisons.hpp:320
Definition: dry-comparisons.hpp:131
Definition: dry-comparisons.hpp:94
constexpr auto and_all(F &&f) const
Definition: dry-comparisons.hpp:105
constexpr auto or_all(F &&f) const
Definition: dry-comparisons.hpp:100
Definition: dry-comparisons.hpp:210
friend std::ostream & operator<<(std::ostream &os, const none_of &self)
Definition: dry-comparisons.hpp:265
Class extending the tuple class from the standard library. Original reason is to allow printing of th...
Definition: tuple.hpp:42
typename printable< T >::type printable_t
Definition: dry-comparisons.hpp:59
Definition: sigHandler.cpp:20
none_of(T &&...) -> none_of< T... >
all_of(T &&...) -> all_of< T... >
any_of(T &&...) -> any_of< T... >
Definition: FordFulkerson.hpp:16
Definition: dry-comparisons.hpp:63
Definition: dry-comparisons.hpp:51