// // bucket_hash_set.hpp // bucket_hash_set // // Created by Sam Jaffe on 2/16/17. // #pragma once #include #include #include #include #include #include "iterator/recursive_iterator.hpp" template , typename KeyEqual = std::equal_to > class bucket_hash_set; template class bucket_hash_set { public: // typedefs using key_type = K; using value_type = key_type const; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using hasher = Hash; using key_equal = KeyEqual; using reference = value_type &; using const_reference = value_type const &; using pointer = value_type *; using const_pointer = value_type const *; using bucket_type = std::list; using impl_type = std::vector; template using iter_wrap = iterator::recursive_iterator; using iterator = iter_wrap; using const_iterator = iter_wrap; using local_iterator = typename bucket_type::const_iterator; using local_const_iterator = typename bucket_type::const_iterator; static const constexpr size_type default_buckets = 10; static const constexpr float default_load_factor = 1.0F; static const constexpr float growth_factor = 2.0F; private: using internal_iterator = iter_wrap; public: // Construction bucket_hash_set() : bucket_hash_set(default_buckets) {} bucket_hash_set(size_type num_buckets, hasher const & hash = hasher(), key_equal const & keq = key_equal()) : buckets_(num_buckets), hasher_(hash), key_equals_(keq) {} template bucket_hash_set(InputIt first, InputIt last, size_type num_buckets = default_buckets, hasher const & hash = hasher(), key_equal const & keq = key_equal()) : bucket_hash_set(num_buckets, hash, keq) { insert(first, last); } bucket_hash_set(std::initializer_list && ilist, size_type num_buckets = default_buckets, hasher const & hash = hasher(), key_equal const & keq = key_equal()) : bucket_hash_set(num_buckets, hash, keq) { insert(ilist); } // Metadata bool empty() const { return size() == 0; } size_type size() const { return size_; } // Iterators const_iterator begin() const { return cbegin(); } const_iterator cbegin() const { return make_recursive_iterator(buckets_); } const_iterator end() const { return cend(); } const_iterator cend() const { return const_iterator{}; } // Create std::pair insert(value_type const & vt) { maybe_expand(1); auto lookup = lookup_impl(buckets_, vt); auto const end = lookup.first->end(); bool const create = lookup.second == end; if ( create ) { ++size_; lookup.second = lookup.first->insert(end, vt); } return { {::iterator::in_place, make_end_aware_iterator(lookup.first, buckets_.end()), make_end_aware_iterator(lookup.second, end)}, create }; } const_iterator insert(const_iterator, value_type const & vt) { return insert(vt).first; } template void insert(InputIt first, InputIt last) { maybe_expand(static_cast(std::distance(first, last))); while (first != last) { insert(*first++); } } void insert(std::initializer_list ilist) { insert(ilist.begin(), ilist.end()); } // Access const_iterator find(key_type const & key) const { return find_impl(buckets_, key); } std::pair equal_range(key_type const & key) const { auto it = find(key); return { it, ++const_iterator(it) }; } size_type count(key_type const & key) const { return find(key) != end(); } // Remove const_iterator erase(const_iterator it) { return erase_impl(unconst_iterator(it)); } const_iterator erase(const_iterator first, const_iterator last) { internal_iterator it = unconst_iterator(first); while (last != it) { it = erase_impl(it); } return it; } size_type erase(key_type const & key) { size_type old_size = size_; erase(find(key)); return old_size - size_; // 1 or 0 } void clear() { erase(begin(), end()); } // Bucket Interaction Functions local_const_iterator begin(size_type bkt) const { return buckets_[bkt].cbegin(); } local_const_iterator cbegin(size_type bkt) const { return buckets_[bkt].cbegin(); } local_const_iterator end(size_type bkt) const { return buckets_[bkt].cend(); } local_const_iterator cend(size_type bkt) const { return buckets_[bkt].cend(); } size_type bucket_count() const { return buckets_.size(); } size_type bucket_size(size_type bkt) const { return buckets_[bkt].size(); } size_type bucket(key_type const & key) const { return hasher_(key) % bucket_count(); } // Hash Policy float load_factor() const { return static_cast(size()) / bucket_count(); } float max_load_factor() const { return max_load_; } void max_load_factor(float max_load) { max_load_ = max_load; } void rehash(size_type buckets) { buckets = std::max({1UL, buckets, static_cast(size() / max_load_factor())}); bucket_hash_set next{buckets, hasher_, key_equals_}; next.max_load_factor(max_load_factor()); next.insert(begin(), end()); swap(next); } void reserve(size_type count) { rehash(static_cast(count / max_load_factor())); } void swap( bucket_hash_set & other ) { using std::swap; swap(size_, other.size_); swap(buckets_, other.buckets_); swap(max_load_, other.max_load_); swap(hasher_, other.hasher_); swap(key_equals_, other.key_equals_); } friend void swap(bucket_hash_set & lhs, bucket_hash_set & rhs) { lhs.swap(rhs); } private: void maybe_expand(size_type add) { if ( static_cast(size() + add) / bucket_count() > max_load_factor() ) { reserve(std::max(size() + add, static_cast(size() * growth_factor))); } } template auto lookup_impl(Bucket & bkt, key_type const & key) const -> std::pairbegin())> { auto listit = bkt.begin() + static_cast(bucket(key)); auto it = search(listit, key); return {listit, it}; } template auto search(Iterator lit, key_type const & key) const -> decltype(lit->begin()) { for ( auto it = lit->begin(); it != lit->end(); ++it ) { if ( key_equals_(key, *it) ) { return it; } } return lit->end(); } template Iterator find_impl(Bucket & bkt, key_type const & key) const { auto lookup = lookup_impl(bkt, key); if (lookup.second == lookup.first->end()) { return Iterator{}; } else { return { ::iterator::in_place, make_end_aware_iterator(lookup.first, bkt.end()), make_end_aware_iterator(lookup.second, lookup.first->end()) }; } } internal_iterator unconst_iterator(const_iterator const & it) { auto lit = std::get<0>(it); auto iter = std::get<1>(it); if ( lit.done() ) { return {}; } auto nit = buckets_.begin(); std::advance(nit, std::distance(buckets_.cbegin(), lit.current())); return { ::iterator::in_place, make_end_aware_iterator(nit, buckets_.end()), make_end_aware_iterator(nit->erase(iter.current(), iter.current()), nit->end()) }; } internal_iterator erase_impl(internal_iterator it) { auto b = std::get<0>(it); auto l = std::get<1>(it); if ( b.done() || l.done() ) { return it; } --size_; return { ::iterator::in_place, make_end_aware_iterator(b.current(), b.end()), make_end_aware_iterator(b->erase(l.current()), l.end()) }; } private: friend bool operator==(bucket_hash_set const & lhs, bucket_hash_set const & rhs) { if (lhs.size() != rhs.size()) { return false; } for (auto & val : lhs) { if (rhs.count(val) == 0) { return false; } } return true; } friend bool operator!=(bucket_hash_set const & lhs, bucket_hash_set const & rhs) { return !(lhs == rhs); } private: // members impl_type buckets_{default_buckets}; hasher hasher_; key_equal key_equals_; //allocator_type alloc_; size_type size_{0}; float max_load_{default_load_factor}; };