AnySet Documentation
AnySet.h
1 #ifndef ANY_SET_H
2 #define ANY_SET_H
3 
4 #ifdef _MSC_VER
5 # include <iso646.h>
6 #endif
7 
8 #include <functional>
9 #include <cstddef>
10 #include <cmath>
11 #include "AnyList.h"
12 #include <vector>
13 #include <algorithm>
14 #include <numeric>
15 #include <type_traits>
16 #include <bitset>
17 #include "AnyHash.h"
18 #include "CompressedPair.h"
19 
43 
45 namespace te {
46 
47 namespace detail {
48 
49 template <typename T, typename = void>
51 {
52  static constexpr const bool value = false;
53 };
54 
55 template <typename T>
56 struct is_iterator<T, std::enable_if_t<!std::is_same_v<typename std::iterator_traits<T>::value_type, void>>>
57 {
58  static constexpr const bool value = true;
59 };
60 
61 template <class T>
62 inline constexpr const bool is_iterator_v = is_iterator<T>::value;
63 
64 } /* namespace detail */
65 
99 template <
100  class HashFn = AnyHash,
101  class KeyEqual = std::equal_to<>,
103 >
104 struct AnySet:
105  private CompressedPair<HashFn, KeyEqual>
106 {
107 private:
110  using list_iterator = typename list_type::iterator;
111  using const_list_iterator = typename list_type::const_iterator;
112 
113 public:
125  struct iterator:
126  public list_iterator
127  {
128  using value_type = typename AnySet::value_type;
129  using reference = typename AnySet::reference;
130  using pointer = typename AnySet::pointer;
131  using difference_type = typename AnySet::difference_type;
133 
134  using list_iterator::list_iterator;
135  using list_iterator::operator=;
136 
137  iterator(const list_iterator& it):
138  list_iterator(it)
139  {
140 
141  }
142  private:
143 
144  iterator to_non_const() const
145  {
146  return iterator(
147  static_cast<const list_iterator&>(*this).to_non_const()
148  );
149  }
150  friend struct AnySet;
151  };
152 
165  public const_list_iterator
166  {
167  using value_type = typename AnySet::value_type;
168  using reference = typename AnySet::const_reference;
169  using pointer = typename AnySet::const_pointer;
170  using difference_type = typename AnySet::difference_type;
172 
173  const_iterator(const const_list_iterator& it):
174  const_list_iterator(it)
175  {
176 
177  }
178 
179  const_iterator(const list_iterator& it):
180  const_list_iterator(it)
181  {
182 
183  }
184 
185  const_iterator& operator=(const iterator& other)
186  {
187  return *this = const_iterator(other);
188  }
189 
190  using const_list_iterator::const_list_iterator;
191  using const_list_iterator::operator=;
192  private:
193 
194  iterator to_non_const() const
195  {
196  return iterator(
197  static_cast<const const_list_iterator&>(*this).to_non_const()
198  );
199  }
200  friend struct AnySet;
201  };
202 
203 private:
204  using table_allocator = typename std::allocator_traits<Allocator>::template rebind_alloc<iterator>;
207  using vector_iterator = typename vector_type::iterator;
208  using const_vector_iterator = typename vector_type::const_iterator;
209 
210  template <class T>
211  struct KeyInfo {
212  const T& value;
213  const std::size_t hash;
214  std::size_t bucket;
215  };
216 
217 public:
221  using size_type = typename vector_type::size_type;
223  using difference_type = typename vector_type::difference_type;
225  using key_equal = KeyEqual;
227  using hasher = HashFn;
229  using allocator_type = Allocator;
235  using const_reference = const value_type&;
237  using pointer = value_type*;
239  using const_pointer = const value_type*;
243 
244 private:
245  static size_type next_highest_pow2(size_type n)
246  {
247  size_type i = 1;
248  while(i <= n)
249  {
250  assert(i * 2 > i);
251  i *= 2;
252  }
253  return i;
254  }
255 
256  allocator_type alloc_socca() const
257  {
259  get_allocator()
260  );
261  }
262 
263  template <bool IsConst>
264  struct BucketIterator:
265  private std::conditional_t<IsConst, const_iterator, iterator>
266  {
267  private:
268  using self_type = BucketIterator<IsConst>;
269  using base_type = std::conditional_t<IsConst, const_iterator, iterator>;
270  public:
271  using value_type = typename base_type::value_type;
272  using reference = typename base_type::reference;
273  using pointer = typename base_type::pointer;
274  using difference_type = typename base_type::difference_type;
275  using iterator_category = typename base_type::iterator_category;
276 
277  BucketIterator() = default;
278 
279  friend bool operator==(const BucketIterator<IsConst>& left, const BucketIterator<IsConst>& right)
280  {
281  if((left.bucket_ != right.bucket_) or (left.mask_ != right.mask_))
282  {
283  // Different buckets or different masks.
284  return false;
285  }
286  if(
287  (left.get_pos().is_null() or left.is_past_the_end())
288  and (right.get_pos().is_null() or right.is_past_the_end())
289  )
290  {
291  return true;
292  }
293  return left.get_pos() == right.get_pos();
294  }
295 
296  friend bool operator!=(const BucketIterator<IsConst>& left, const BucketIterator<IsConst>& right)
297  { return not (left == right); }
298 
299  self_type& operator++()
300  {
301  ++get_pos();
302  return *this;
303  }
304 
305  self_type operator++() const
306  {
307  auto cpy = *this;
308  ++*this;
309  return cpy;
310  }
311 
312  using base_type::operator->;
313  using base_type::operator*;
314 
315  private:
316  BucketIterator(base_type pos, std::size_t bucket, std::size_t table_size):
317  base_type(pos), bucket_(bucket), mask_(table_size - 1u)
318  {
319 
320  }
321 
322  bool is_past_the_end() const
323  {
324  assert(not get_pos().is_null());
325  // Returns true if 'pos_' is past-the-end for the whole list or the element pointed to by
326  // 'pos_' has a hash in a different bucket (past-the-end for this bucket).
327  return get_pos().is_end() or ((get_pos()->hash & mask_) != bucket_);
328  }
329 
330  BucketIterator<false> to_non_const() const
331  {
332  return BucketIterator<false>(
333  get_pos().to_non_const(), bucket_, mask_ + 1
334  );
335  }
336 
337  const base_type& get_pos() const
338  { return static_cast<const base_type&>(*this); }
339 
340  base_type& get_pos()
341  { return static_cast<base_type&>(*this); }
342 
343  std::size_t bucket_{0};
344  std::size_t mask_{0};
345  template <class, class, class>
346  friend struct AnySet;
347  };
348 
349 public:
351  using local_iterator = BucketIterator<false>;
353  using const_local_iterator = BucketIterator<true>;
354 
355  void _assert_invariants(bool check_load_factor = false) const
356  {
357  list_._assert_invariants();
358  assert(table_size() > 0u);
359  assert(((table_size() & (table_size() - 1)) == 0u) and "Table size not a power of two.");
360  assert(max_load_factor_ > 0.0);
361  auto iter_dist = std::distance(cbegin(), cend());
362  assert(iter_dist >= 0);
363  assert(static_cast<size_type>(iter_dist) == size());
364  assert(std::all_of(table_.begin(), table_.end(), [](const auto& v){ return v.is_null() or not v.is_end();}));
365  for(size_type i = 0; i < bucket_count(); ++i)
366  {
367  // each bucket is sorted WRT hash values of the nodes in the bucket
368  assert(std::is_sorted(
369  this->begin(i),
370  this->end(i),
371  [](const auto& l, const auto& r){ return l.hash < r.hash; }
372  ));
373  assert(std::all_of(begin(i), end(i), [&](const auto& v){ return bucket_index(v.hash) == i; }));
374  }
375  // the load factor is allowed to be not satisfied if the user changed the max_load_factor().
376  // we only do this assertion when asked to
377  if(check_load_factor)
378  assert(load_factor_satisfied());
379  }
380 
381  ~AnySet() = default;
382 
385 
390 
398  explicit AnySet(
399  size_type bucket_count,
400  const HashFn& hash = HashFn(),
401  const KeyEqual& equal = KeyEqual(),
402  const Allocator& alloc = Allocator()
403  ):
404  pair_type(hash, equal),
405  table_(next_highest_pow2(bucket_count), iterator(), allocator_type(alloc))
406  {
407 
408  }
409 
415  AnySet(size_type bucket_count, const Allocator& alloc):
416  AnySet(bucket_count, HashFn(), KeyEqual(), alloc)
417  {
418 
419  }
420 
427  AnySet(size_type bucket_count, const HashFn& hash, const Allocator& alloc):
428  AnySet(bucket_count, hash, KeyEqual(), alloc)
429  {
430 
431  }
432 
447  template <class InputIt>
449  InputIt first,
450  InputIt last,
451  size_type bucket_count = 0,
452  const HashFn& hash = HashFn(),
453  const KeyEqual& equal = KeyEqual(),
454  const Allocator& alloc = Allocator()
455  ):
456  AnySet(bucket_count, hash, equal, alloc)
457  {
458  insert(first, last);
459  }
460 
473  template <class InputIt>
475  InputIt first,
476  InputIt last,
477  size_type bucket_count,
478  const Allocator& alloc
479  ):
480  AnySet(first, last, bucket_count, HashFn(), KeyEqual(), alloc)
481  {
482 
483  }
484 
498  template <class InputIt>
500  InputIt first,
501  InputIt last,
502  size_type bucket_count,
503  const HashFn& hash,
504  const Allocator& alloc
505  ):
506  AnySet(first, last, bucket_count, hash, KeyEqual(), alloc)
507  {
508 
509  }
510 
518  AnySet(const AnySet& other, const Allocator& alloc):
519  pair_type(other.as_pair()),
520  list_(other.list_),
521  table_(0, alloc),
522  max_load_factor_(other.max_load_factor_)
523  {
524  table_.assign(other.table_.size(), iterator());
525  list_type tmp(std::move(list_));
526  assert(size() == 0u);
527  node_handle node{nullptr};
528  while(not tmp.empty())
529  {
530  node = std::move(tmp.pop(tmp.begin()).first);
531  node = std::move(push(std::move(node)));
532  assert(not static_cast<bool>(node));
533  }
534  assert(tmp.empty());
535  }
536 
544  AnySet(const AnySet& other):
545  pair_type(other.as_pair()),
546  list_(other.list_, list_type::make_copy),
547  table_(0, other.alloc_socca()),
548  max_load_factor_(other.max_load_factor_)
549  {
550  table_.resize(other.table_.size());
551  list_type tmp(std::move(list_));
552  assert(size() == 0u);
553  node_handle node{nullptr};
554  while(not tmp.empty())
555  {
556  node = std::move(tmp.pop(tmp.begin()).first);
557  node = std::move(push(std::move(node)).second);
558  assert(not static_cast<bool>(node));
559  }
560  assert(tmp.empty());
561  }
562 
570  AnySet(AnySet&& other, const Allocator& alloc):
571  pair_type(std::move(other.as_pair())),
572  list_(std::move(other.list_)),
573  table_(std::move(other.table_), alloc),
574  max_load_factor_(other.max_load_factor_)
575  {
576  fix_table_after_move();
577  assert(other.empty());
578  if(other.table_.size() == 0u)
579  other.table_.assign(1u, iterator());
580  }
581 
589  AnySet(AnySet&& other):
590  pair_type(std::move(other.as_pair())),
591  list_(std::move(other.list_)),
592  table_(std::move(other.table_)),
593  max_load_factor_(other.max_load_factor_)
594  {
595  fix_table_after_move();
596  assert(other.empty());
597  if(other.table_.size() == 0u)
598  other.table_.assign(1u, iterator());
599  }
600 
615  template <class T>
618  size_type bucket_count = 0,
619  const HashFn& hash = HashFn(),
620  const KeyEqual& equal = KeyEqual(),
621  const Allocator& alloc = Allocator()
622  ):
623  AnySet(ilist.begin(), ilist.end(), bucket_count, hash, equal, alloc)
624  {
625 
626  }
627 
640  template <class T>
643  size_type bucket_count,
644  const Allocator& alloc
645  ):
646  AnySet(ilist, bucket_count, HashFn(), KeyEqual(), alloc)
647  {
648 
649  }
650 
664  template <class T>
667  size_type bucket_count,
668  const HashFn& hash,
669  const Allocator& alloc
670  ):
671  AnySet(ilist, bucket_count, hash, KeyEqual(), alloc)
672  {
673 
674  }
675 
689  template <class ... T>
691  std::tuple<T ...>&& tup,
692  size_type bucket_count = 2 * (sizeof...(T)),
693  const HashFn& hash = HashFn(),
694  const KeyEqual& equal = KeyEqual(),
695  const Allocator& alloc = Allocator()
696  ):
697  AnySet(bucket_count, hash, equal, alloc)
698  {
699  std::apply(
700  [&](auto&& ... args) {
701  this->arg_insert(std::forward<decltype(args)>(args)...);
702  },
703  std::move(tup)
704  );
705 
706  }
707 
719  template <class ... T>
721  std::tuple<T ...>&& tup,
722  size_type bucket_count,
723  const Allocator& alloc
724  ):
725  AnySet(std::move(tup), bucket_count, HashFn(), KeyEqual(), alloc)
726  {
727 
728  }
729 
742  template <class ... T>
744  std::tuple<T ...>&& tup,
745  size_type bucket_count,
746  const HashFn& hash,
747  const Allocator& alloc
748  ):
749  AnySet(std::move(tup), bucket_count, hash, KeyEqual(), alloc)
750  {
751 
752  }
753 
755 
758 
770  AnySet& operator=(const AnySet& other)
771  { return *this = std::move(self_type(other)); }
772 
781  AnySet& operator=(AnySet&& other) noexcept(std::is_nothrow_move_assignable_v<vector_type>)
782  {
783  as_pair() = std::move(other.as_pair());
784  list_ = std::move(other.list_);
785  table_ = std::move(other.table_);
786  max_load_factor_ = std::move(other.max_load_factor_);
787  fix_table_after_move();
788  assert(other.empty());
789  if(other.table_.size() == 0u)
790  other.table_.assign(1u, iterator());
791  return *this;
792  }
793 
801  template <class T>
803  { return *this = self_type(ilist); }
804 
806 
809 
816  { return list_.cbegin(); }
817 
824  { return cbegin(); }
825 
832  { return list_.begin(); }
833 
840  { return list_.cend(); }
841 
848  { return cend(); }
849 
856  { return list_.end(); }
857 
859 
862 
868  [[nodiscard]]
869  bool empty() const noexcept
870  { return not static_cast<bool>(size()); }
871 
877  size_type size() const noexcept
878  { return list_.size(); }
879 
887  size_type max_size() const noexcept
889 
891 
894 
899  void clear() noexcept
900  {
901  list_.clear();
902  std::fill(table_.begin(), table_.end(), iterator());
903  }
904 
928  template <class T, class ... Args>
930  {
931  static_assert(
932  std::is_same_v<T, std::decay_t<T>>,
933  "Cannot emplace references, arrays, or functions into an AnySet."
934  );
935  auto node(make_any_value<T, HashFn, KeyEqual>(get_hasher(), std::forward<Args>(args)...));
936  auto hash_v = node->hash;
937  KeyInfo<T> ki{unsafe_cast<const T&>(*node), hash_v, bucket_index(hash_v)};
938  return insert_impl<true>(ki.value, ki, std::move(node));
939  }
940 
969  template <class T, class ... Args>
970  std::pair<iterator, bool> emplace_hint([[maybe_unused]] const_iterator hint, Args&& ... args)
971  { return emplace<T>(std::forward<Args>(args)...); }
972 
986  template <class T>
988  {
989  return insert_impl<true>(std::forward<T>(value), make_key_info(value));
990  }
991 
1008  template <class T>
1009  std::pair<iterator, bool> insert([[maybe_unused]] const_iterator hint, T&& value)
1010  { return insert(std::forward<T>(value)); }
1011 
1027  template <
1028  class It,
1029  class = std::enable_if_t<
1030  std::is_copy_constructible_v<typename std::iterator_traits<It>::value_type>
1031  or std::is_rvalue_reference_v<decltype(*std::declval<It>())>
1032  >
1033  >
1034  size_type insert(It first, It last)
1035  {
1036  using iter_cat = typename std::iterator_traits<It>::iterator_category;
1037  return range_insert(first, last, iter_cat{});
1038  }
1039 
1058  template <
1059  class T,
1060  class U,
1061  class ... V,
1062  class = std::enable_if_t<
1063  (not (std::is_same_v<std::decay_t<T>, std::decay_t<U>> and detail::is_iterator_v<std::decay_t<T>>))
1064  and (not std::is_same_v<std::decay_t<T>, const_iterator>)
1065  and (not std::is_same_v<std::decay_t<T>, iterator>)
1066  >
1067  >
1068  std::bitset<2ull + sizeof...(V)> insert(T&& first, U&& second, V&& ... args)
1069  {
1070  return arg_insert(
1071  std::forward<T>(first),
1072  std::forward<U>(second),
1073  std::forward<V>(args) ...
1074  );
1075  }
1076 
1093  template <class T, class = std::enable_if_t<std::is_copy_constructible_v<T>>>
1095  {
1096  return insert(ilist.begin(), ilist.end());
1097  }
1098 
1111  {
1112  return pop(pos).second;
1113  }
1114 
1131  {
1132  iterator pos = first.to_non_const();
1133  if(first == last)
1134  return pos;
1135  bool done = false;
1136  do {
1137  // hop over 'pos' and check if we've reached the end
1138  auto nxt = std::next(first);
1139  done = (nxt == last);
1140  pos = erase(pos);
1141  } while(not done);
1142  return pos.to_non_const();
1143  }
1144 
1157  template <class T, class = std::enable_if_t<not (std::is_same_v<const_iterator, T> or std::is_same_v<iterator, T>)>>
1158  size_type erase(const T& value)
1159  {
1160  auto [pos, found] = find_position(make_key_info(value));
1161  if(found)
1162  erase(pos);
1163  return static_cast<size_type>(found);
1164  }
1165 
1177  void swap(AnySet& other)
1178  noexcept(std::is_nothrow_swappable_v<vector_type> and std::is_nothrow_swappable_v<pair_type>)
1179  {
1180  using std::swap;
1181  swap(as_pair(), other.as_pair());
1182  swap(list_, other.list_);
1183  swap(table_, other.table_);
1184  swap(max_load_factor_, other.max_load_factor_);
1185  this->fix_table_after_move();
1186  other.fix_table_after_move();
1187  }
1188 
1202  AnySet& update(const AnySet& other)
1203  {
1204  preemptive_reserve(other.size());
1205  for(const auto& any_v: other)
1206  {
1207  auto [pos, found] = find_position(make_key_info(any_v));
1208  if(found)
1209  {
1210  continue;
1211  }
1212  else
1213  {
1214  unsafe_splice_at(pos, any_v.clone());
1215  }
1216  }
1217  assert(load_factor_satisfied());
1218  return *this;
1219  }
1220 
1245  {
1246  preemptive_reserve(other.size());
1247  for(auto pos = other.begin(); pos != other.end(); )
1248  {
1249  auto [ins_pos, found] = find_position(make_key_info(*pos));
1250  if(found)
1251  {
1252  ++pos;
1253  }
1254  else
1255  {
1256  node_handle node;
1257  std::tie(node, pos) = other.pop(pos);
1258  unsafe_splice_at(ins_pos, std::move(node));
1259  }
1260  }
1261  return *this;
1262  }
1263 
1265 
1268 
1278  template <class T>
1279  size_type count(const T& value) const
1280  {
1281  return static_cast<size_type>(
1282  find_position(make_key_info(value)).second
1283  );
1284  }
1285 
1294  template <class T>
1295  const_iterator find(const T& value) const
1296  {
1297  auto [pos, found] = find_position(make_key_info(value));
1298  if(found)
1299  return pos;
1300  else
1301  return cend();
1302  }
1303 
1312  template <class T>
1313  iterator find(const T& value)
1314  {
1315  return static_cast<const self_type*>(this)->find(value).to_non_const();
1316  }
1317 
1328  template <class T>
1330  {
1331  auto pos = find(value);
1332  if(pos != cend())
1333  return std::make_pair(pos, std::next(pos));
1334  else
1335  return std::make_pair(cend(), cend());
1336  }
1337 
1348  template <class T>
1350  {
1351  auto [first, last] = static_cast<const self_type*>(this)->equal_range(value);
1352  return std::make_pair(
1353  first.to_non_const(),
1354  last.to_non_const()
1355  );
1356  }
1357 
1367  bool contains_value(const value_type& any_v) const
1368  {
1369  return contains(any_v);
1370  }
1371 
1381  bool contains_value_eq(const value_type& any_v) const
1382  { return find_matching_value(any_v) != cend(); }
1383 
1393  template <class T>
1394  bool contains(const T& value) const
1395  {
1396  return count(value);
1397  }
1398 
1407  template <class T>
1408  bool contains_eq(const T& value) const
1409  { return find_matching_value(value) != cend(); }
1410 
1412 
1415 
1424  {
1425  assert(buck < bucket_count());
1426  return const_local_iterator(table_[buck], buck, table_size());
1427  }
1428 
1437  { return cbegin(buck); }
1438 
1447  { return cbegin(buck).to_non_const(); }
1448 
1459  {
1460  assert(buck < bucket_count());
1461  return const_local_iterator(const_iterator(), buck, table_size());
1462  }
1463 
1474  { return cend(buck); }
1475 
1486  { return cend(buck).to_non_const(); }
1487 
1493  size_type bucket_count() const noexcept
1494  { return table_size(); }
1495 
1501  size_type max_bucket_count() const noexcept
1502  {
1503  size_type maxm = ~((~size_type(0)) >> 1);
1504  while(maxm > table_.max_size())
1505  maxm >>= 1;
1506  return maxm;
1507  }
1508 
1517  {
1518  assert(buck < bucket_count());
1519  return static_cast<size_type>(
1520  std::distance(cbegin(buck), cend(buck))
1521  );
1522  }
1523 
1531  template <class T>
1532  size_type bucket(const T& value) const
1533  { return bucket_index(get_hasher()(value)); }
1534 
1536 
1539 
1547  void max_load_factor(float f)
1548  {
1549  assert(f > 0.0);
1550  max_load_factor_ = f;
1551  }
1552 
1558  float max_load_factor() const noexcept
1559  { return max_load_factor_; }
1560 
1566  float load_factor() const noexcept
1567  { return static_cast<double>(size()) / table_size(); }
1568 
1580  void rehash(size_type nbuckets)
1581  {
1582  size_type bcount = bucket_count();
1583  assert(bcount > 0u);
1584  auto load_factor_good = [&]() {
1585  return (static_cast<double>(size()) / bcount) <= max_load_factor_;
1586  };
1587  if(nbuckets < bcount)
1588  {
1589  if(nbuckets == 0u)
1590  nbuckets = 1u;
1591  while(nbuckets < bcount and load_factor_good())
1592  bcount /= 2;
1593  if((bcount == bucket_count()) and load_factor_good())
1594  return;
1595 
1596  }
1597  else if(nbuckets > bcount)
1598  {
1599  assert(max_bucket_count() >= nbuckets);
1600  while(nbuckets > bcount)
1601  {
1602  assert(bcount > 0u);
1603  bcount *= 2;
1604  }
1605  }
1606  while(not load_factor_good())
1607  bcount *= 2;
1608  if(bcount > bucket_count())
1609  grow_table(bcount);
1610  else if(bcount < bucket_count())
1611  shrink_table(bcount);
1612  }
1613 
1625  void reserve(size_type count)
1626  {
1627  rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
1628  }
1629 
1631 
1634 
1641  { return get_hasher(); }
1642 
1649  { return get_key_equal(); }
1650 
1657  { return table_.get_allocator(); }
1658 
1660 
1663 
1670  friend void swap(AnySet& left, AnySet& right)
1671  { return left.swap(right); }
1672 
1686  friend bool operator==(const AnySet& left, const AnySet& right)
1687  {
1688  if(left.size() != right.size())
1689  return false;
1690  // iterate over the set with the smaller table.
1691  const auto& iter_set = (left.table_size() > right.table_size()) ? right : left;
1692  // search through the set with the larger table
1693  const auto& search_set = (&left == &iter_set) ? right : left;
1694  for(const auto& item: iter_set)
1695  {
1696  auto pos = search_set.find_matching_value(item);
1697  if(pos.is_end())
1698  {
1699  return false;
1700  }
1701  }
1702  return true;
1703  }
1704 
1718  friend bool operator!=(const AnySet& left, const AnySet& right)
1719  { return not (left == right); }
1720 
1722 
1725 
1744  {
1745  assert(not pos_.is_end());
1746  assert(not pos_.is_null());
1747 
1748  iterator pos = pos_.to_non_const();
1749  size_type buck_idx = iter_bucket_index(pos);
1750  iterator bucket_head = table_[buck_idx];
1751 
1752  assert(not bucket_head.is_null());
1753  assert(not bucket_head.is_end());
1754 
1755  if(bucket_head == pos)
1756  {
1757  // 'pos' is the first item in its bucket.
1758  auto [node, next] = std::move(list_.pop(pos));
1759  if(next.is_end())
1760  {
1761  // 'pos' was the last item in 'list_'. The bucket is now empty.
1762  table_[buck_idx] = iterator();
1763  return std::make_pair(std::move(node), end());
1764  }
1765 
1766  if(size_type next_idx = iter_bucket_index(next); next_idx != buck_idx)
1767  {
1768  // The const_iterator after 'bucket_head' is in a different bucket.
1769  // 'pos' was the last item in its bucket and the bucket is now empty.
1770  // Since we just invalidated all iterators to the element following
1771  // 'pos', we need to fix iterator in the bucket whose first element
1772  // was pointed to by next(pos).
1773  table_[buck_idx] = iterator();
1774  // Fix the iterator we invalidated.
1775  table_[next_idx] = next;
1776  }
1777  else
1778  {
1779  // 'pos' was *not* the last element in its bucket. The iterator
1780  // stored in the bucket should automagically point to the next
1781  // item. Nothing to fix.
1782  assert(table_[buck_idx] == next);
1783  }
1784  return std::make_pair(std::move(node), next);
1785  }
1786  else
1787  {
1788  // 'pos' is *not* the first item in its bucket.
1789  auto [node, next] = std::move(list_.pop(pos));
1790  if(next.is_end())
1791  {
1792  // 'pos' was the last item in the whole list. Nothing to fix.
1793  return std::make_pair(std::move(node), end());
1794  }
1795 
1796  if(size_type next_idx = iter_bucket_index(next); next_idx != buck_idx)
1797  {
1798  // 'pos' was the last item in its bucket.
1799  // Since we just invalidated all iterators to the element following
1800  // 'pos', we need to fix iterator in the bucket whose first element
1801  // was pointed to by next(pos).
1802  table_[next_idx] = next;
1803  }
1804  else
1805  {
1806  // 'pos' was *not* the last element in its bucket. The iterator
1807  // stored in the bucket should automagically point to the next
1808  // item. Nothing to fix.
1809  assert(table_[buck_idx] == next);
1810  }
1811  return std::make_pair(std::move(node), next);
1812  }
1813  }
1814 
1827  { return pos->clone(); }
1828 
1842  {
1843  auto [pos, found] = find_position(make_key_info(*node));
1844  if(found)
1845  return std::make_pair(pos.to_non_const(), std::move(node));
1846  iterator ins_pos = safely_splice_at(pos, std::move(node));
1847  return std::make_pair(ins_pos, node_handle(nullptr));
1848  }
1849 
1850 
1872  { return splice_or_copy(std::move(other), pos); }
1873 
1893  { return splice_or_copy(std::move(other), first, last); }
1894 
1921  template <class T, class = std::enable_if_t<std::is_same_v<std::decay_t<T>, self_type>>>
1922  auto splice_or_copy(T&& other, const_iterator first, const_iterator last)
1923  -> std::pair<decltype(other.begin()), decltype(other.begin())>
1924  {
1925  if(first == last)
1926  return std::make_pair(first.to_non_const(), last.to_non_const());
1927  preemptive_reserve(std::distance(first, last));
1928  auto pos = first;
1929  bool done = false;
1930  do {
1931  done = (std::next(pos) == last);
1932  std::tie(std::ignore, pos, std::ignore) = splice_or_copy(std::forward<T>(other), pos);
1933  } while(not done);
1934  return std::make_pair(first.to_non_const(), pos.to_non_const());
1935  }
1936 
1965  template <class T, class = std::enable_if_t<std::is_same_v<std::decay_t<T>, self_type>>>
1966  auto splice_or_copy(T&& other, const_iterator pos)
1967  -> std::tuple<iterator, decltype(other.begin()), bool>
1968  {
1969  using tuple_t = std::tuple<iterator, decltype(other.begin()), bool>;
1970  assert(not pos.is_end());
1971  auto ki = make_key_info(*pos);
1972  auto [ins_pos_, found] = find_position(ki);
1973  auto ins_pos = ins_pos_.to_non_const();
1974  if(found)
1975  return tuple_t(ins_pos, std::next(pos).to_non_const(), false);
1976  if constexpr(
1977  std::is_rvalue_reference_v<decltype(other)>
1978  and not std::is_const_v<std::remove_reference_t<decltype(other)>>
1979  )
1980  {
1981  auto [node, next] = other.pop(pos);
1982  try
1983  {
1984  assert(load_factor_satisfied());
1985  return tuple_t(
1986  safely_splice_at(ins_pos, ki, std::move(node)), next.to_non_const(), true
1987  );
1988  }
1989  catch(...)
1990  {
1991  // put back the node if didn't make it in
1992  if(node)
1993  other.unsafe_splice_at(pos, std::move(node));
1994  throw;
1995  }
1996  }
1997  else
1998  {
1999  assert(load_factor_satisfied());
2000  return tuple_t(
2001  safely_splice_at(ins_pos, ki, other.dup(pos)),
2002  std::next(pos).to_non_const(),
2003  true
2004  );
2005  }
2006  }
2007 
2009 
2010 private:
2011 
2012  void fix_table_after_move()
2013  {
2014  if(size() > 0u)
2015  table_[iter_bucket_index(begin())] = begin();
2016  }
2017 
2018  float load_factor(size_type extra) const noexcept
2019  { return static_cast<double>(size() + extra) / table_size(); }
2020 
2021  bool load_factor_satisfied(size_type extra = 0) const
2022  { return load_factor(extra) <= max_load_factor(); }
2023 
2024  iterator safely_splice_at(const_iterator pos, node_handle&& node)
2025  {
2026  return safely_splice_at(pos, make_key_info(*node), std::move(node));
2027  }
2028 
2029  template <class Value>
2030  iterator safely_splice_at(const_iterator pos, const KeyInfo<Value>& ki, node_handle&& node)
2031  {
2032  // Insert the node, *then* check if a rehash is needed. This optimizes for the case
2033  // where we don't rehash (which is almost certainly the common case) and doing it this
2034  // way results in simpler code.
2035  iterator ins_pos = unsafe_splice_at(pos, ki, std::move(node));
2036 
2037  if(not load_factor_satisfied())
2038  {
2039  // However, we want rollback semantics if resizing the bucket table throws an exception.
2040  // Use the scope guard pattern instead of a try-catch-rethrow. This lets the compiler possibly
2041  // optimize the guard out entirely if callers don't have an exception handler (many
2042  // implementations only propagate exceptions when there's a handler beneath the throw).
2043  struct RollbackScopeGuard {
2044  self_type& self; // *this
2045  const_iterator pos; // iterator to erase
2046  bool good = false; // whether we
2047 
2048  ~RollbackScopeGuard()
2049  {
2050  // If growing failed, erase the value we just inserted.
2051  // This gives us rollback/commit semantics and only pessimizes
2052  // the uncommon case.
2053  if(not good)
2054  // Control flow only reach here if an exception is thrown by 'grow_table'.
2055  self.erase(pos);
2056  }
2057  } guard{*this, pos, false};
2058 
2059  // Before we grow the table, save the address of the inserted node, we'll need it
2060  // to find the node again after rehashing.
2061  const value_type* addr_save = std::addressof(*ins_pos);
2062 
2063  grow_table(2 * table_size());
2064 
2065  // No exceptions thrown.
2066  guard.good = true;
2067 
2068  // the key landed somewhere else now, go find it to give the caller their iterator.
2069  auto buck_idx = bucket_index(ki.hash);
2070  auto buck_pos = table_[buck_idx];
2071  assert(not buck_pos.is_null());
2072  assert(not buck_pos.is_end());
2073  while(std::addressof(*buck_pos) != addr_save)
2074  {
2075  ++buck_pos;
2076  assert(iter_bucket_index(buck_pos) == buck_idx);
2077  }
2078  return buck_pos.to_non_const();
2079  }
2080  else
2081  {
2082  // No rehash, just return ins_pos.
2083  return ins_pos;
2084  }
2085  }
2086 
2087  template <class Value>
2088  iterator initialize_bucket(const KeyInfo<Value>& ki, node_handle&& node)
2089  {
2090  assert(table_[ki.bucket].is_null());
2091  return table_[ki.bucket] = list_.push_back(std::move(node));
2092  }
2093 
2094  iterator unsafe_splice_at(const_iterator pos, node_handle&& node)
2095  {
2096  return unsafe_splice_at(pos, make_key_info(*node), std::move(node));
2097  }
2098 
2099  template <class Value>
2100  iterator unsafe_splice_at(const_iterator pos, const KeyInfo<Value>& ki, node_handle&& node)
2101  {
2102  if(pos.is_null())
2103  return initialize_bucket(ki, std::move(node));
2104  else if(pos.is_end())
2105  return list_.splice(pos, std::move(node));
2106 
2107  size_type buck_idx = iter_bucket_index(pos);
2108  iterator ins_pos = list_.splice(pos, std::move(node));
2109  if(buck_idx != ki.bucket)
2110  {
2111  // The iterator that we just inserted changed the value of an iterator
2112  // in another bucket. The iterator whose value changed should now point
2113  // to the 'next' of the node we just inserted. Make it so.
2114  auto next_pos = std::next(ins_pos);
2115  assert(iter_bucket_index(next_pos) == buck_idx);
2116  table_[buck_idx] = next_pos;
2117  }
2118  return ins_pos;
2119  }
2120 
2121  template <class Value>
2122  bool insertion_compare(const Value& value, const value_type& any_v) const
2123  { return compare(value, any_v, get_key_equal()); }
2124 
2125  template <class Value>
2126  bool insertion_compare(const value_type& any_v, const Value& value) const
2127  { return compare(any_v, value, get_key_equal()); }
2128 
2129  template <class Value>
2130  std::size_t get_hash_value(const Value& value) const
2131  {
2132  if constexpr(std::is_same_v<Value, value_type>)
2133  return value.hash;
2134  else
2135  return get_hasher()(value);
2136  }
2137 
2138  size_type iter_bucket_index(const_iterator pos) const
2139  {
2140  assert(not pos.is_end());
2141  return bucket_index(pos->hash);
2142  }
2143 
2144  template <class Value>
2145  KeyInfo<Value> make_key_info(const Value& val) const
2146  {
2147  return make_key_info(val, get_hash_value(val));
2148  }
2149 
2150  template <class Value>
2151  KeyInfo<Value> make_key_info(const Value& val, std::size_t hash_v) const
2152  {
2153  return KeyInfo<Value>{val, hash_v, bucket_index(hash_v)};
2154  }
2155 
2156  template <class Value>
2157  const_iterator find_matching_value(const Value& value) const
2158  {
2159  auto ki = make_key_info(value);
2160  auto [pos, last] = get_bucket_start(ki);
2161  if(pos.is_null() or (not last.is_null()))
2162  return cend();
2163 
2164  while((not pos.is_end()) and (pos->hash == ki.hash))
2165  {
2166  if(*pos == value)
2167  return const_iterator(pos);
2168  ++pos;
2169  }
2170  return cend();
2171  }
2172 
2173  template <class ... T>
2174  std::bitset<sizeof...(T)> arg_insert(T&& ... args)
2175  {
2176  std::bitset<sizeof...(T)> bs;
2177  std::size_t pos = 0;
2178  preemptive_reserve(sizeof...(args));
2179 
2180  // set the ith bit if the ith arg was inserted
2181  (bs.set(pos++, insert_impl<false>(std::forward<T>(args)).second) , ...);
2182 
2183  assert(load_factor() <= max_load_factor());
2184  return bs;
2185  }
2186 
2187  template <class It>
2188  size_type range_insert(It first, It last, std::input_iterator_tag)
2189  {
2190  size_type count = 0;
2191  while(first != last)
2192  count += static_cast<size_type>(insert_impl<true>(*first++).second);
2193  return count;
2194  }
2195 
2196  template <class It, class = std::enable_if_t<std::is_copy_constructible_v<typename std::iterator_traits<It>::value_type>>>
2197  size_type range_insert(It first, It last, std::forward_iterator_tag)
2198  {
2199  size_type count = 0;
2200  preemptive_reserve(std::distance(first, last));
2201  while(first != last)
2202  count += static_cast<size_type>(insert_impl<false>(*first++).second);
2203  assert(load_factor() <= max_load_factor());
2204  return count;
2205  }
2206 
2207  void preemptive_reserve(std::size_t ins_count)
2208  {
2209  auto new_count = size() + ins_count;
2210  size_type new_table_size = table_size();
2211  assert(new_table_size > 0u);
2212  auto compute_new_load_factor = [&](){
2213  return (static_cast<double>(new_count) / new_table_size);
2214  };
2215  while(compute_new_load_factor() > max_load_factor())
2216  new_table_size *= 2;
2217  if(new_table_size > table_size())
2218  grow_table(new_table_size);
2219  }
2220 
2221  template <bool CheckLoadFactor, class T>
2222  std::pair<iterator, bool> insert_impl(T&& value)
2223  {
2224  return insert_impl<CheckLoadFactor>(
2225  std::forward<T>(value), make_key_info(value), nullptr
2226  );
2227  }
2228 
2229  template <bool CheckLoadFactor, class T>
2230  std::pair<iterator, bool> insert_impl(
2231  T&& value,
2232  const KeyInfo<std::decay_t<T>>& ki,
2233  node_handle existing = nullptr
2234  )
2235  {
2236  using ValueType = std::decay_t<T>;
2237  iterator ins_pos;
2238  auto [pos, found] = find_position(ki);
2239 
2240  if(not found)
2241  {
2242  // if constexpr(std::is_copy_constructible_v<ValueType>)
2243  if constexpr(std::is_constructible_v<ValueType, decltype(value)>)
2244  {
2245  if(not existing)
2246  {
2247  existing = make_any_value<std::decay_t<T>, HashFn, KeyEqual>(
2248  ki.hash, std::forward<T>(value)
2249  );
2250  }
2251  }
2252  else
2253  {
2254  assert(existing);
2255  }
2256 
2257  if constexpr(CheckLoadFactor)
2258  ins_pos = safely_splice_at(pos, ki, std::move(existing));
2259  else
2260  ins_pos = unsafe_splice_at(pos, ki, std::move(existing));
2261  assert(not ins_pos.is_null());
2262  assert(not ins_pos.is_end());
2263  }
2264  else
2265  {
2266  ins_pos = pos.to_non_const();
2267  }
2268  return std::make_pair(ins_pos, not found);
2269  }
2270 
2271  void shrink_table(size_type new_size) noexcept
2272  {
2273  assert(new_size < table_size());
2274  // new size must always be a power of two
2275  assert((new_size & (new_size - 1)) == 0u);
2276 
2277  table_.assign(new_size, iterator());
2278 
2279  list_type tmp = std::move(list_);
2280  assert(size() == 0u);
2281  node_handle node{nullptr};
2282  while(not tmp.empty())
2283  {
2284  node = std::move(tmp.pop(tmp.begin()).first);
2285  node = std::move(push(std::move(node)).second);
2286  assert(not static_cast<bool>(node));
2287  }
2288  assert(tmp.empty());
2289  }
2290 
2291  void grow_table(size_type new_size)
2292  {
2293  assert(new_size > table_size());
2294  // new size must always be a power of two
2295  assert((new_size & (new_size - 1)) == 0u);
2296  table_.assign(new_size, iterator());
2297  list_type tmp = std::move(list_);
2298  assert(size() == 0u);
2299  node_handle node{nullptr};
2300  while(not tmp.empty())
2301  {
2302  node = std::move(tmp.pop(tmp.begin()).first);
2303  node = std::move(push(std::move(node)).second);
2304  assert(not static_cast<bool>(node));
2305  }
2306  assert(tmp.empty());
2307  }
2308 
2317  template <class Value>
2318  std::pair<const_iterator, bool> find_position(const KeyInfo<Value>& ki) const
2319  { return find_position(ki, get_key_equal()); }
2320 
2331  template <class Value, class Comp>
2332  std::pair<const_iterator, bool> find_position(const KeyInfo<Value>& ki, Comp comp) const
2333  {
2334  if(auto [pos, last] = get_bucket_start(ki); pos.is_null() and last.is_null())
2335  {
2336  // Bucket is empty, return null iterator.
2337  return std::make_pair(const_iterator(), false);
2338  }
2339  else if(last.is_null())
2340  {
2341  assert(pos->hash >= ki.hash);
2342  for(; (not pos.is_end()) and pos->hash == ki.hash; ++pos)
2343  {
2344  if(compare(ki.value, *pos, comp))
2345  // found a match
2346  return std::make_pair(pos, true);
2347  }
2348  // No match, return const_iterator to first element whose hash is
2349  // greater than ki's hash, or the end of the list, which ever comes
2350  // first.
2351  return std::make_pair(pos, false);
2352  }
2353  else
2354  {
2355  assert(not pos.is_null());
2356  assert(last == cend() or iter_bucket_index(last) != ki.bucket);
2357  // No match, return end of the bucket.
2358  return std::make_pair(last, false);
2359  }
2360  }
2361 
2374  template <class Value>
2375  std::pair<const_iterator, const_iterator> get_bucket_start(const KeyInfo<Value>& ki) const
2376  {
2377  auto first = table_[ki.bucket];
2378  if(first.is_null())
2379  // two 'null' const_iterators
2381  assert(not first.is_end());
2382  auto pos = first;
2383  std::size_t pos_hash = pos->hash;
2384  do {
2385  if(pos_hash >= ki.hash)
2386  return std::make_pair(pos, const_iterator());
2387  ++pos;
2388  if(pos.is_end())
2389  break;
2390  pos_hash = pos->hash;
2391  } while(bucket_index(pos_hash) == ki.bucket);
2392  return std::make_pair(first, pos);
2393  }
2394 
2395  size_type table_size() const
2396  { return table_.size(); }
2397 
2398  size_type get_mask() const
2399  { return table_size() - 1; }
2400 
2401  size_type bucket_index(std::size_t hash) const
2402  { return hash & get_mask(); }
2403 
2404  bool equal_values(const value_type& left, const value_type& right) const
2405  { return left.compare_to(right, get_key_equal()); }
2406 
2407  // functions to access the hasher and comparator
2408  pair_type& as_pair() &
2409  { return static_cast<pair_type&>(*this); }
2410 
2411  pair_type&& as_pair() &&
2412  { return static_cast<pair_type&&>(*this); }
2413 
2414  const pair_type& as_pair() const &
2415  { return static_cast<const pair_type&>(*this); }
2416 
2417  const pair_type&& as_pair() const &&
2418  { return static_cast<const pair_type&&>(*this); }
2419 
2420  HashFn& get_hasher() &
2421  { return as_pair().first(); }
2422 
2423  HashFn&& get_hasher() &&
2424  { return std::move(std::move(as_pair()).first()); }
2425 
2426  const HashFn& get_hasher() const &
2427  { return as_pair().first(); }
2428 
2429  const HashFn&& get_hasher() const &&
2430  { return std::move(std::move(as_pair()).first()); }
2431 
2432  KeyEqual& get_key_equal() &
2433  { return as_pair().second(); }
2434 
2435  KeyEqual&& get_key_equal() &&
2436  { return std::move(std::move(as_pair()).second()); }
2437 
2438  const KeyEqual& get_key_equal() const &
2439  { return as_pair().second(); }
2440 
2441  const KeyEqual&& get_key_equal() const &&
2442  { return std::move(std::move(as_pair()).second()); }
2443 
2444  list_type list_;
2445  vector_type table_;
2446  float max_load_factor_{1.0};
2447 };
2448 
2463 template <
2464  class HashFn = te::AnyHash,
2465  class KeyEqual = std::equal_to<>,
2467  class ... Elements
2468 >
2469 te::AnySet<HashFn, KeyEqual, Allocator> make_anyset(Elements&& ... elements)
2470 {
2472  std::forward_as_tuple(std::forward<Elements>(elements)...)
2473  );
2474 }
2475 
2476 
2477 } /* namespace te */
2478 
2479 #endif /* ANY_SET_H */
Forward iterator type returned from const operations on AnySet instances.
Definition: AnySet.h:164
AnySet & update(AnySet &&other)
Moves elements from other into the set.
Definition: AnySet.h:1244
AnySet(size_type bucket_count, const Allocator &alloc)
Construct an empty AnySet instance. Sets max_load_factor() to 1.0.
Definition: AnySet.h:415
AnySet(std::tuple< T ... > &&tup, size_type bucket_count, const Allocator &alloc)
Construct an AnySet with the contents of the tuple tup. Sets max_load_factor() to 1...
Definition: AnySet.h:720
BucketIterator< true > const_local_iterator
Const iterator type suitable for traversal through an individual bucket.
Definition: AnySet.h:353
value_type * pointer
Pointer to AnyValue.
Definition: AnySet.h:237
std::pair< const_iterator, const_iterator > equal_range(const T &value) const
Obtain a const_iterator range to the elements that have the same type as, and compares equal to the v...
Definition: AnySet.h:1329
AnyValue< HashFn, KeyEqual > value_type
AnyValue.
Definition: AnySet.h:219
T forward_as_tuple(T... args)
T distance(T... args)
HashFn hasher
Hash function type.
Definition: AnySet.h:227
T tie(T... args)
const_iterator end() const
Get a past-the-end const_iterator for this set.
Definition: AnySet.h:847
T ceil(T... args)
size_type count(const T &value) const
Returns the number of elements with a value that have the same type as, and compare equal to the valu...
Definition: AnySet.h:1279
const value_type * const_pointer
Pointer to const AnyValue.
Definition: AnySet.h:239
T swap(T... args)
Forward iterator type returned from non-const operations on AnySet instances.
Definition: AnySet.h:125
AnySet(InputIt first, InputIt last, size_type bucket_count, const HashFn &hash, const Allocator &alloc)
Construct an AnySet instance from the range [first, last). Sets max_load_factor() to 1...
Definition: AnySet.h:499
bool contains_value(const value_type &any_v) const
Check if this contains the same value as another set.
Definition: AnySet.h:1367
node_handle dup(const_iterator pos) const
Copy and return the element at the position pointed to by pos.
Definition: AnySet.h:1826
size_type insert(std::initializer_list< T > ilist)
Inserts elements from the list ilist that do not already exist in the set. If multiple elements in th...
Definition: AnySet.h:1094
std::pair< iterator, node_handle > push(node_handle &&node)
Insert the value pointed to by node to this.
Definition: AnySet.h:1841
const_iterator begin() const
Get a const_iterator to the first element in the set.
Definition: AnySet.h:823
bool contains_eq(const T &value) const
Check if this contains value.
Definition: AnySet.h:1408
std::pair< iterator, bool > insert([[maybe_unused]] const_iterator hint, T &&value)
Inserts an element into the set, if the set doesn&#39;t already contain an element with an equivalent val...
Definition: AnySet.h:1009
void reserve(size_type count)
Sets the number of buckets to the number needed to accomodate at least count elements without exceedi...
Definition: AnySet.h:1625
friend void swap(AnySet &left, AnySet &right)
Calls left.swap(right).
Definition: AnySet.h:1670
local_iterator end(size_type buck)
Get a local_iterator to the element one past the least element in the bucket at index buck...
Definition: AnySet.h:1485
size_type size() const noexcept
Gets the number of elements in the set, i.e. std::distance(begin(), end()).
Definition: AnySet.h:877
Definition: AnySet.h:50
const_iterator cend() const
Get a past-the-end const_iterator for this set.
Definition: AnySet.h:839
std::pair< iterator, bool > emplace(Args &&... args)
Inserts a new element into the container constructed in-place with the given args if there is no elem...
Definition: AnySet.h:929
void rehash(size_type nbuckets)
Sets the number of buckets to the smallest possible value that is at least as large as nbuckets and r...
Definition: AnySet.h:1580
size_type bucket_count() const noexcept
Get the number of buckets in the set.
Definition: AnySet.h:1493
value_type & reference
Reference to AnyValue.
Definition: AnySet.h:233
AnySet(std::tuple< T ... > &&tup, size_type bucket_count, const HashFn &hash, const Allocator &alloc)
Construct an AnySet with the contents of the tuple tup. Sets max_load_factor() to 1...
Definition: AnySet.h:743
const_local_iterator cend(size_type buck) const
Get a const_local_iterator to the element one past the least element in the bucket at index buck...
Definition: AnySet.h:1458
size_type max_bucket_count() const noexcept
Get the maximum possible number of buckets in the set.
Definition: AnySet.h:1501
typename vector_type::difference_type difference_type
Difference type.
Definition: AnySet.h:223
size_type bucket_size(size_type buck) const
Get the number of elements in the bucket at index buck.
Definition: AnySet.h:1516
AnySet(AnySet &&other, const Allocator &alloc)
Move constructs an AnySet instance from other. Constructs the set with the copy of the contents of ot...
Definition: AnySet.h:570
const_local_iterator end(size_type buck) const
Get a const_local_iterator to the element one past the least element in the bucket at index buck...
Definition: AnySet.h:1473
AnySet(InputIt first, InputIt last, size_type bucket_count, const Allocator &alloc)
Construct an AnySet instance from the range [first, last). Sets max_load_factor() to 1...
Definition: AnySet.h:474
bool empty() const noexcept
Checks if the set has no elements, i.e. whether begin() == end().
Definition: AnySet.h:869
AnySet(AnySet &&other)
Copy constructs an AnySet instance from other. Constructs the set with the copy of the contents of ot...
Definition: AnySet.h:589
AnySet(InputIt first, InputIt last, size_type bucket_count=0, const HashFn &hash=HashFn(), const KeyEqual &equal=KeyEqual(), const Allocator &alloc=Allocator())
Construct an AnySet instance from the range [first, last). Sets max_load_factor() to 1...
Definition: AnySet.h:448
key_equal key_eq() const
Get a copy of the equality comparison function.
Definition: AnySet.h:1648
const_iterator cbegin() const
Get a const_iterator to the first element in the set.
Definition: AnySet.h:815
AnySet(size_type bucket_count, const HashFn &hash, const Allocator &alloc)
Construct an empty AnySet instance. Sets max_load_factor() to 1.0.
Definition: AnySet.h:427
T next(T... args)
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
Definition: CompressedPair.h:47
AnySet & operator=(AnySet &&other) noexcept(std::is_nothrow_move_assignable_v< vector_type >)
Move assigns the contents of this AnySet instance from the contents of other. Moves the load factor...
Definition: AnySet.h:781
std::pair< iterator, iterator > splice(AnySet &other, const_iterator first, const_iterator last)
Moves the elements in the range [first, last) from other into this.
Definition: AnySet.h:1892
AnySet(size_type bucket_count, const HashFn &hash=HashFn(), const KeyEqual &equal=KeyEqual(), const Allocator &alloc=Allocator())
Construct an empty AnySet instance. Sets max_load_factor() to 1.0.
Definition: AnySet.h:398
float max_load_factor() const noexcept
Get the maximum allowable load factor for the set.
Definition: AnySet.h:1558
std::bitset< 2ull+sizeof...(V)> insert(T &&first, U &&second, V &&... args)
Inserts args if they do not already exist in the set. If multiple values in args have the same type a...
Definition: AnySet.h:1068
T make_pair(T... args)
T addressof(T... args)
AnySet(std::initializer_list< T > ilist, size_type bucket_count, const HashFn &hash, const Allocator &alloc)
Construct an AnySet with the contents of the initializer list ilist. Same as AnySet(init.begin(), init.end()). Sets max_load_factor() to 1.0. If multiple elements in the list compare equivalent, only the first encountered is inserted.
Definition: AnySet.h:665
std::pair< iterator, iterator > equal_range(const T &value)
Obtain an iterator range to the elements that have the same type as, and compares equal to the value...
Definition: AnySet.h:1349
void clear() noexcept
Removes all elements from the container. Invalidates any references, pointers, or iterators referring...
Definition: AnySet.h:899
T max(T... args)
T move(T... args)
T select_on_container_copy_construction(T... args)
std::pair< iterator, bool > emplace_hint([[maybe_unused]] const_iterator hint, Args &&... args)
Inserts a new element into the container constructed in-place with the given args if there is no elem...
Definition: AnySet.h:970
std::tuple< iterator, iterator, bool > splice(AnySet &other, const_iterator pos)
Moves the element at position pos from other into this.
Definition: AnySet.h:1871
std::pair< iterator, bool > insert(T &&value)
Inserts an element into the set, if the set doesn&#39;t already contain an element with an equivalent val...
Definition: AnySet.h:987
const_local_iterator cbegin(size_type buck) const
Get a const_local_iterator to the first element in the bucket at index buck.
Definition: AnySet.h:1423
T size(T... args)
local_iterator begin(size_type buck)
Get a local_iterator to the first element in the bucket at index buck.
Definition: AnySet.h:1446
iterator erase(const_iterator pos)
Remove the element at the position pointed to by pos from the set.
Definition: AnySet.h:1110
friend bool operator==(const AnySet &left, const AnySet &right)
Compare the contents of two sets.
Definition: AnySet.h:1686
AnySet(const AnySet &other)
Copy constructs an AnySet instance from other. Constructs the set with the copy of the contents of ot...
Definition: AnySet.h:544
T is_sorted(T... args)
typename vector_type::size_type size_type
Size type.
Definition: AnySet.h:221
AnySet & operator=(const AnySet &other)
Copy assigns the contents of this AnySet instance from the contents of other. Copies the load factor...
Definition: AnySet.h:770
const_local_iterator begin(size_type buck) const
Get a const_local_iterator to the first element in the bucket at index buck.
Definition: AnySet.h:1436
AnySet(std::initializer_list< T > ilist, size_type bucket_count=0, const HashFn &hash=HashFn(), const KeyEqual &equal=KeyEqual(), const Allocator &alloc=Allocator())
Construct an AnySet with the contents of the initializer list ilist. Same as AnySet(init.begin(), init.end()). Sets max_load_factor() to 1.0. If multiple elements in the list compare equivalent, only the first encountered is inserted.
Definition: AnySet.h:616
AnySet & operator=(std::initializer_list< T > ilist)
Assigns the contents of this AnySet instance with the contents of ilist.
Definition: AnySet.h:802
T all_of(T... args)
iterator begin()
Get a iterator to the first element in the set.
Definition: AnySet.h:831
size_type max_size() const noexcept
Get the maximum number of elements the container is able to hold due to system or library implementat...
Definition: AnySet.h:887
Generic hash function object.
Definition: AnyHash.h:107
const_iterator find(const T &value) const
Obtain a const_iterator to the element that has the same type as, and compares equal to the value...
Definition: AnySet.h:1295
iterator find(const T &value)
Obtain an iterator to the element that has the same type as, and compares equal to the value...
Definition: AnySet.h:1313
size_type bucket(const T &value) const
Get the bucket index of value.
Definition: AnySet.h:1532
allocator_type get_allocator() const
Get a copy of the allocator.
Definition: AnySet.h:1656
AnySet is an associative container that contains a set of unique objects of any constructible type...
Definition: AnySet.h:104
iterator end()
Get a past-the-end iterator for this set.
Definition: AnySet.h:855
KeyEqual key_equal
Key equality comparator type.
Definition: AnySet.h:225
AnySet(std::tuple< T ... > &&tup, size_type bucket_count=2 *(sizeof...(T)), const HashFn &hash=HashFn(), const KeyEqual &equal=KeyEqual(), const Allocator &alloc=Allocator())
Construct an AnySet with the contents of the tuple tup. Sets max_load_factor() to 1...
Definition: AnySet.h:690
Definition: AnyNode.h:93
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
AnySet()
Constructs an empty set with 1 bucket. Sets max_load_factor() to 1.0.
Definition: AnySet.h:389
T fill(T... args)
float load_factor() const noexcept
Get the load factor for the set.
Definition: AnySet.h:1566
BucketIterator< false > local_iterator
Iterator type suitable for traversal through an individual bucket.
Definition: AnySet.h:351
const value_type & const_reference
Reference to const AnyValue.
Definition: AnySet.h:235
AnySet(const AnySet &other, const Allocator &alloc)
Copy constructs an AnySet instance from other. Constructs the set with the copy of the contents of ot...
Definition: AnySet.h:518
void swap(AnySet &other) noexcept(std::is_nothrow_swappable_v< vector_type > and std::is_nothrow_swappable_v< pair_type >)
Exchanges the contents of the set with those of other. Does not invoke any move, copy, or swap operations on individual elements. Additionally exchanges the hash functions, comparison functions, and max_load_factor()s of the sets.
Definition: AnySet.h:1177
AnySet(std::initializer_list< T > ilist, size_type bucket_count, const Allocator &alloc)
Construct an AnySet with the contents of the initializer list ilist. Same as AnySet(init.begin(), init.end()). Sets max_load_factor() to 1.0. If multiple elements in the list compare equivalent, only the first encountered is inserted.
Definition: AnySet.h:641
size_type erase(const T &value)
Remove the element from the set whose type is the same type as value and whose value compares equal t...
Definition: AnySet.h:1158
iterator erase(const_iterator first, const_iterator last)
Remove elements in the range [first, last).
Definition: AnySet.h:1130
auto splice_or_copy(T &&other, const_iterator pos) -> std::tuple< iterator, decltype(other.begin()), bool >
Copies or moves the element at position pos from other into this.
Definition: AnySet.h:1966
bool contains_value_eq(const value_type &any_v) const
Check if this contains the same value as another set.
Definition: AnySet.h:1381
hasher hash_function() const
Get a copy of the hash function.
Definition: AnySet.h:1640
T forward(T... args)
friend bool operator!=(const AnySet &left, const AnySet &right)
Compare the contents of two sets.
Definition: AnySet.h:1718
Allocator allocator_type
Allocator type.
Definition: AnySet.h:229
AnySet & update(const AnySet &other)
Add copies of elements from other.
Definition: AnySet.h:1202
bool contains(const T &value) const
Check if this contains value.
Definition: AnySet.h:1394
size_type insert(It first, It last)
Inserts elements from the range [first, last) that do not already exist in the set. If multiple elements in the range have values that compare equivalent, and there is no such element already in the set, only the first encountered is inserted.
Definition: AnySet.h:1034
void max_load_factor(float f)
Set the maximum possible number of buckets in the set.
Definition: AnySet.h:1547