AnySet Documentation
SetOperations.h
Go to the documentation of this file.
1 #ifndef SET_OPERATIONS_H
2 #define SET_OPERATIONS_H
3 
4 #ifdef _MSC_VER
5 # include <iso646.h>
6 #endif
7 
8 #include "AnySet.h"
9 #include <type_traits>
10 
20 
21 namespace te {
22 
23 namespace detail {
24 
25 template <class T>
26 struct is_any_set: public std::false_type {};
27 
28 template <class H, class E, class A>
29 struct is_any_set<AnySet<H, E, A>>: public std::true_type {};
30 
31 template <class T>
32 inline constexpr const bool is_any_set_v = is_any_set<T>::value;
33 
34 template <class Op, class Pred, class T, class ... U>
35 decltype(auto) select_and_invoke(Op&& op, [[maybe_unused]] Pred pred, T&& first)
36 {
37  return std::invoke(std::forward<Op>(op), std::forward<T>(first));
38 }
39 
40 
41 template <class Op, class Pred, class T, class U, class ... Args>
42 decltype(auto) select_and_invoke(Op&& op, Pred pred, T&& first, U&& second, Args&& ... args)
43 {
44  using std::forward;
45  using std::invoke;
46  if(pred(static_cast<const T&>(first), static_cast<const U&>(second)))
47  {
48  return select_and_invoke(
49  // bind 'first' to 'op's second argument position
50  [&op, &first](auto&& f, auto&& ... a) {
51  return invoke(
52  forward<Op>(op),
53  forward<decltype(f)>(f), forward<T>(first), forward<decltype(a)>(a)...
54  );
55  },
56  pred, forward<U>(second), forward<Args>(args) ...
57  );
58  }
59  else
60  {
61  return select_and_invoke(
62  // bind 'second' to 'op's second argument position
63  [&op, &second](auto&& f, auto&& ... a) {
64  return invoke(
65  forward<Op>(op),
66  forward<decltype(f)>(f), forward<U>(second), forward<decltype(a)>(a)...
67  );
68  },
69  pred, forward<T>(first), forward<Args>(args) ...
70  );
71  }
72 }
73 
74 } /* namespace detail */
75 
78 
96 template <
97  class T,
98  class ... U,
99  class = std::enable_if_t<
100  detail::is_any_set_v<std::decay_t<T>>
101  and (std::is_same_v<std::decay_t<T>, std::decay_t<U>> and ...)
102  >
103 >
104 std::decay_t<T> union_of(T&& first, U&& ... args)
105 {
106  // Avoid an ICE in gcc-7.x
107 #if !(defined(__GNUC__) && (__GNUC__ == 7))
108  // this 'const' causes an internal compiler error in gcc-7.x. I like const enough
109  // to do this #if and even make a comment about it.
110  const
111 #endif
112  std::size_t max_total_size = (first.size() + ... + args.size());
113  auto is_better = [](auto&& left, auto&& right) {
114  // return true if 'right' is the better candidate
115  constexpr bool left_is_rvalue = std::is_rvalue_reference_v<decltype(left)>;
116  constexpr bool right_is_rvalue = std::is_rvalue_reference_v<decltype(right)>;
117  if constexpr(left_is_rvalue != right_is_rvalue)
118  return right_is_rvalue;
119  else
120  return left.bucket_count() < right.bucket_count();
121  };
122  auto union_of_impl = [=](auto&& f, auto&& ... sets) -> std::decay_t<T> {
123  std::decay_t<T> result(std::forward<decltype(f)>(f));
124  result.max_load_factor(1.0);
125  result.reserve(max_total_size);
126  (result.update(std::forward<decltype(sets)>(sets)) , ...);
127  return result;
128  };
129  return detail::select_and_invoke(
130  union_of_impl, is_better, std::forward<T>(first), std::forward<U>(args)...
131  );
132 }
133 
151 template <
152  class T,
153  class ... U,
154  class = std::enable_if_t<
155  detail::is_any_set_v<std::decay_t<T>>
156  and (std::is_same_v<std::decay_t<T>, std::decay_t<U>> and ...)
157  >
158 >
159 std::decay_t<T> intersection_of(T&& first, U&& ... args)
160 {
161  auto is_better = [](auto&& left, auto&& right) {
162  // return true if 'right' is the better candidate
163  constexpr bool left_is_rvalue = std::is_rvalue_reference_v<decltype(left)>;
164  constexpr bool right_is_rvalue = std::is_rvalue_reference_v<decltype(right)>;
165  if constexpr(left_is_rvalue != right_is_rvalue)
166  return right_is_rvalue;
167  else
168  return left.size() > right.size();
169  };
170  auto intersection_of_impl = [](auto&& f, const auto& ... set) -> std::decay_t<T> {
171  std::decay_t<T> result(std::forward<decltype(f)>(f));
172  result.max_load_factor(1.0);
173  auto pos = result.begin();
174  while(pos != result.end())
175  {
176  const auto& any_v = *pos;
177  auto has_value = [&](const auto& s) { return s.contains_value(any_v); };
178  if(not static_cast<bool>((has_value(set) and ...)))
179  {
180  pos = result.erase(pos);
181  }
182  else
183  {
184  ++pos;
185  }
186  }
187  return result;
188  };
189  return detail::select_and_invoke(
190  intersection_of_impl, is_better, std::forward<T>(first), std::forward<U>(args)...
191  );
192 }
193 
194 
212 template <
213  class T,
214  class ... U,
215  class = std::enable_if_t<
216  detail::is_any_set_v<std::decay_t<T>>
217  and (std::is_same_v<std::decay_t<T>, std::decay_t<U>> and ...)
218  >
219 >
220 std::decay_t<T> symmetric_difference_of(T&& first, U&& ... args)
221 {
222  auto is_better = [](auto&& left, auto&& right) {
223  // return true if 'right' is the better candidate
224  constexpr bool left_is_rvalue = std::is_rvalue_reference_v<decltype(left)>;
225  constexpr bool right_is_rvalue = std::is_rvalue_reference_v<decltype(right)>;
226  if constexpr(left_is_rvalue != right_is_rvalue)
227  return right_is_rvalue;
228  else
229  return left.bucket_count() < right.bucket_count();
230  };
231  auto symmetric_difference_of_impl = [](auto&& f, auto&& ... set) -> std::decay_t<T> {
232  std::decay_t<std::decay_t<T>> result(std::forward<decltype(f)>(f));
233  result.max_load_factor(1.0);
234  auto inplace_symdiff = [&](auto&& s) {
235  result ^= std::forward<decltype(s)>(s);
236  };
237  (... , inplace_symdiff(std::forward<decltype(set)>(set)));
238  return result;
239  };
240  return detail::select_and_invoke(
241  symmetric_difference_of_impl, is_better, std::forward<T>(first), std::forward<U>(args)...
242  );
243 }
244 
262 template <
263  class T,
264  class ... U,
265  class = std::enable_if_t<
266  detail::is_any_set_v<std::decay_t<T>>
267  and (std::is_same_v<std::decay_t<T>, std::decay_t<U>> and ...)
268  >
269 >
270 std::decay_t<T> difference_of(T&& left, const U& ... right)
271 {
272  if constexpr(std::is_rvalue_reference_v<decltype(left)>)
273  {
274  std::decay_t<T> result(std::move(left));
275  result.max_load_factor(1.0);
276 
277  for(auto pos = result.begin(); pos != result.end();)
278  {
279  if(((right.contains_value(*pos)) or ...))
280  {
281  pos = result.erase(pos);
282  }
283  else
284  {
285  ++pos;
286  }
287  }
288  return result;
289  }
290  else
291  {
292  std::decay_t<T> result(left.bucket_count());
293 
294  for(auto pos = left.begin(); pos != left.end(); ++pos)
295  {
296  if(not ((right.contains_value(*pos)) or ...))
297  result.push(left.dup(pos));
298  }
299  return result;
300  }
301 }
302 
314 template <class H, class E, class A>
315 bool is_subset_of(const AnySet<H, E, A>& sub, const AnySet<H, E, A>& super)
316 {
317  if(sub.size() > super.size())
318  return false;
319  for(const auto& v: sub)
320  {
321  if(not super.contains_value(v))
322  return false;
323  }
324  return true;
325 }
326 
338 template <class H, class E, class A>
339 bool is_superset_of(const AnySet<H, E, A>& super, const AnySet<H, E, A>& sub)
340 { return is_subset_of(sub, super); }
341 
342 
344 
347 
350 
352 template <
353  class T, class U,
354  class = std::enable_if_t<
355  detail::is_any_set_v<std::decay_t<T>>
356  and std::is_same_v<std::decay_t<T>, std::decay_t<U>>
357  >
358 >
359 std::decay_t<T> operator+(T&& left, U&& right)
360 { return union_of(std::forward<T>(left), std::forward<U>(right)); }
361 
363 template <class H, class E, class A>
365 { return left.update(std::move(right)); }
366 
368 template <class H, class E, class A>
370 { return left.update(right); }
371 
372 
374 template <
375  class T, class U,
376  class = std::enable_if_t<
377  detail::is_any_set_v<std::decay_t<T>>
378  and std::is_same_v<std::decay_t<T>, std::decay_t<U>>
379  >
380 >
381 std::decay_t<T> operator|(T&& left, U&& right)
382 { return std::forward<T>(left) + std::forward<U>(right); }
383 
385 template <class H, class E, class A>
387 { return left += std::move(right); }
388 
390 template <class H, class E, class A>
392 { return left += right; }
393 
395 
398 
399 
401 template <
402  class T, class U,
403  class = std::enable_if_t<
404  detail::is_any_set_v<std::decay_t<T>>
405  and std::is_same_v<std::decay_t<T>, std::decay_t<U>>
406  >
407 >
408 std::decay_t<T> operator&(T&& left, U&& right)
409 { return intersection_of(std::forward<T>(left), std::forward<U>(right)); }
410 
412 template <class H, class E, class A>
414 { return left = intersection_of(std::move(left), std::move(right)); }
415 
417 template <class H, class E, class A>
419 { return left = intersection_of(std::move(left), right); }
420 
422 
425 
426 
428 template <
429  class T, class U,
430  class = std::enable_if_t<
431  detail::is_any_set_v<std::decay_t<T>>
432  and std::is_same_v<std::decay_t<T>, std::decay_t<U>>
433  >
434 >
435 std::decay_t<T> operator-(T&& left, U&& right)
436 { return difference_of(std::forward<T>(left), std::forward<U>(right)); }
437 
439 template <class H, class E, class A>
441 { return left = difference_of(std::move(left), std::move(right)); }
442 
444 template <class H, class E, class A>
446 { return left = difference_of(std::move(left), right); }
447 
448 
450 
453 
455 template <
456  class T, class U,
457  class = std::enable_if_t<
458  detail::is_any_set_v<std::decay_t<T>>
459  and std::is_same_v<std::decay_t<T>, std::decay_t<U>>
460  >
461 >
462 std::decay_t<T> operator^(T&& left, U&& right)
463 { return symmetric_difference_of(std::forward<T>(left), std::forward<U>(right)); }
464 
466 template <
467  class H, class E, class A, class U,
468  class = std::enable_if_t<std::is_same_v<AnySet<H, E, A>, std::decay_t<U>>>
469 >
471 {
472  for(auto pos = right.begin(); pos != right.end();)
473  {
474  auto [ins_pos, next, success] = left.splice_or_copy(
475  std::forward<U>(right), pos
476  );
477  pos = next;
478  if(not success)
479  left.pop(ins_pos);
480  }
481  return left;
482 }
483 
485 
488 
490 template <class H, class E, class A>
491 bool operator<=(const AnySet<H, E, A>& left, const AnySet<H, E, A>& right)
492 { return is_subset_of(left, right); }
493 
495 template <class H, class E, class A>
496 bool operator<(const AnySet<H, E, A>& left, const AnySet<H, E, A>& right)
497 { return is_subset_of(left, right) and (left.size() < right.size()); }
498 
500 template <class H, class E, class A>
501 bool operator>=(const AnySet<H, E, A>& left, const AnySet<H, E, A>& right)
502 { return is_superset_of(left, right); }
503 
505 template <class H, class E, class A>
506 bool operator>(const AnySet<H, E, A>& left, const AnySet<H, E, A>& right)
507 { return is_superset_of(left, right) and (left.size() > right.size()); }
508 
510 
513 
515 template <class H, class E, class C>
516 std::ostream& operator<<(std::ostream& os, const AnySet<H, E, C>& set)
517 {
518  os << '{';
519  if(set.size() > 0)
520  {
521  auto pos = set.begin();
522  os << *pos++;
523  for(auto stop = set.end(); pos != stop; ++pos)
524  os << ", " << *pos;
525  }
526  os << '}';
527  return os;
528 }
529 
531 
532 
533 } /* namespace te */
534 
535 
536 
537 
538 #endif /* SET_OPERATIONS_H */
std::decay_t< T > operator|(T &&left, U &&right)
Compute the union of left and right as if by union_of(left, right);
Definition: SetOperations.h:381
AnySet< H, E, A > & operator|=(AnySet< H, E, A > &left, AnySet< H, E, A > &&right)
Compute the union of left and right as if by left = (left + right);
Definition: SetOperations.h:386
AnySet< H, E, A > & operator+=(AnySet< H, E, A > &left, AnySet< H, E, A > &&right)
Compute the union of left and right as if by left = (left + right);
Definition: SetOperations.h:364
bool contains_value(const value_type &any_v) const
Check if this contains the same value as another set.
Definition: AnySet.h:1367
bool is_subset_of(const AnySet< H, E, A > &sub, const AnySet< H, E, A > &super)
Determine whether sub is a subset of super.
Definition: SetOperations.h:315
bool is_superset_of(const AnySet< H, E, A > &super, const AnySet< H, E, A > &sub)
Determine whether super is a superset of sub.
Definition: SetOperations.h:339
size_type size() const noexcept
Gets the number of elements in the set, i.e. std::distance(begin(), end()).
Definition: AnySet.h:877
std::decay_t< T > intersection_of(T &&first, U &&... args)
Get the intersection of a group of AnySet instances.
Definition: SetOperations.h:159
std::decay_t< T > operator-(T &&left, U &&right)
Compute the (asymmetric) difference of left and right as if by difference_of(left, right);
Definition: SetOperations.h:435
std::decay_t< T > difference_of(T &&left, const U &... right)
Get the (asymmetric) difference of a group of AnySet instances.
Definition: SetOperations.h:270
AnySet< H, E, A > & operator^=(AnySet< H, E, A > &left, U &&right)
Compute the symmetric difference of left and right as if by left = (left ^ right); ...
Definition: SetOperations.h:470
std::decay_t< T > operator &(T &&left, U &&right)
Compute the intersection of left and right as if by intersection_of(left, right); ...
Definition: SetOperations.h:408
auto splice_or_copy(T &&other, const_iterator first, const_iterator last) -> std::pair< decltype(other.begin()), decltype(other.begin())>
Copies or moves the elements in the range [first, last) from other into this.
Definition: AnySet.h:1922
Primary classes and utility functions for AnySet.
Definition: SetOperations.h:21
std::decay_t< T > operator^(T &&left, U &&right)
Compute the symmetric difference of left and right as if by symmetric_difference_of(left, right);
Definition: SetOperations.h:462
AnySet< H, E, A > & operator &=(AnySet< H, E, A > &left, AnySet< H, E, A > &&right)
Compute the intersection of left and right as if by left = (left & right);
Definition: SetOperations.h:413
bool operator>(const AnySet< H, E, A > &left, const AnySet< H, E, A > &right)
Semantically equivalent to is_subset_of(left, right) && !(left.size() == right.size());.
Definition: SetOperations.h:506
T move(T... args)
std::decay_t< T > symmetric_difference_of(T &&first, U &&... args)
Get the symmetric difference of a group of AnySet instances.
Definition: SetOperations.h:220
AnySet< H, E, A > & operator-=(AnySet< H, E, A > &left, AnySet< H, E, A > &&right)
Compute the (asymmetric) difference of left and right as if by left = (left - right); ...
Definition: SetOperations.h:440
std::decay_t< T > union_of(T &&first, U &&... args)
Get the union of a group of AnySet instances.
Definition: SetOperations.h:104
bool operator>=(const AnySet< H, E, A > &left, const AnySet< H, E, A > &right)
Semantically equivalent to is_superset_of(left, right);.
Definition: SetOperations.h:501
AnySet is an associative container that contains a set of unique objects of any constructible type...
Definition: AnySet.h:104
std::pair< node_handle, iterator > pop(const_iterator pos_)
Remove and return the element at the position pointed to by pos from the set.
Definition: AnySet.h:1743
std::decay_t< T > operator+(T &&left, U &&right)
Compute the union of left and right as if by union_of(left, right);
Definition: SetOperations.h:359
T forward(T... args)
Definition: SetOperations.h:26
AnySet & update(const AnySet &other)
Add copies of elements from other.
Definition: AnySet.h:1202