// =====================================================================================================================
// open-cpp-utils, an open-source cpp library with data structures that extend the STL.
// Copyright (C) 2024 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
// =====================================================================================================================
#ifndef OPEN_CPP_UTILS_HASH_TABLE_H
#define OPEN_CPP_UTILS_HASH_TABLE_H
#include
#include
#include
#include
#include "math.h"
#include "optional.h"
namespace open_cpp_utils
{
template, class Alloc = std::allocator>
class hash_table
{
// Typedefs ============================================================================================================
public:
friend class iterator;
private:
struct _Node;
public:
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using hash_type = Hash;
using allocator_type = typename std::allocator_traits::template rebind_alloc<_Node>;
using size_type = size_t;
using iterator_type = class iterator;
private:
using table_type = _Node*;
using node = int64_t;
static constexpr node nullnode = std::integral_constant{};
// Structs =============================================================================================================
private:
struct _Node
{
optional value;
int psl;
_Node() : value(), psl(0) { }
_Node(const_reference v) : value(v), psl(0) { }
_Node(const_reference v, int psl) : value(v), psl(psl) { }
bool operator==(const _Node& b) const
{
return value == b.value && psl == b.psl;
}
};
// Iterators ===========================================================================================================
public:
class iterator
{
public:
iterator(hash_table* table, size_type idx) : table_(table), idx_(idx) { _next_index(); }
iterator(const iterator& b) : table_(b.table_), idx_(b.idx_) { }
iterator(iterator&& b) noexcept : table_(b.table_), idx_(b.idx_) { }
~iterator() = default;
iterator& operator=(const iterator& b) { if(&b == this) return *this; table_ = b.table_; idx_ = b.idx_; return *this; }
iterator& operator=(iterator&& b) noexcept { if(&b == this) return *this; table_ = b.table_; idx_ = b.idx_; return *this; };
iterator& operator++() { ++idx_; _next_index(); return *this; }
iterator operator++(int) { iterator ret = *this; ++idx_; _next_index(); return ret; }
bool operator==(const iterator& o) const = default;
bool operator!=(const iterator& o) const = default;
reference operator*() { return table_->table_[idx_].value; }
pointer operator->() { return &*table_->table_[idx_].value; }
const_reference operator*() const { return table_->table_[idx_].value; }
const_pointer operator->() const { return &*table_->table_[idx_].value; }
private:
void _next_index()
{
while(idx_ < table_->capacity_ && not table_->table_[idx_].value())
++idx_;
}
hash_table* table_;
size_type idx_;
};
class const_iterator
{
public:
const_iterator(const hash_table* table, node idx) : table_(table), idx_(idx) { _next_index(); }
const_iterator(const const_iterator& b) : table_(b.table_), idx_(b.idx_) { }
const_iterator(const_iterator&& b) noexcept : table_(b.table_), idx_(b.idx_) { }
~const_iterator() = default;
const_iterator& operator=(const const_iterator& b) { if(&b == this) return *this; table_ = b.table_; idx_ = b.idx_; return *this; }
const_iterator& operator=(const_iterator&& b) noexcept { if(&b == this) return *this; table_ = b.table_; idx_ = b.idx_; return *this; };
const_iterator& operator++() { ++idx_; _next_index(); return *this; }
const_iterator operator++(int) { const_iterator ret = *this; ++idx_; _next_index(); return ret; }
bool operator==(const const_iterator& o) const = default;
bool operator!=(const const_iterator& o) const = default;
reference operator*() const { return table_->table_[idx_].value; }
pointer operator->() const { return &*table_->table_[idx_].value; }
private:
void _next_index() { while(idx_ < table_->capacity_ && not table_->table_[idx_].value()) ++idx_; }
const hash_table* table_;
node idx_;
};
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, capacity_); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator end() const { return const_iterator(this, capacity_); }
// Functions ===========================================================================================================
public:
// Constructors & Destructor -------------------------------------------------------------------------------------------
hash_table() : table_(nullptr), capacity_(0), size_(0), load_factor_(0.8), hash_(), alloc_() { }
hash_table(std::initializer_list data);
hash_table(const hash_table&);
hash_table(hash_table&&) = default; // Default Move Constructor should suffice
~hash_table() { clear(); }
// Modifiers -----------------------------------------------------------------------------------------------------------
void reserve(size_t size);
void clear();
void insert(const_reference x);
void erase(const_reference x);
bool contains(const_reference x);
iterator find(const_reference x)
{
node res = _find(x);
if(res == nullnode) res = capacity_;
return iterator(this, res);
}
const_iterator find(const_reference x) const
{
node res = _find(x);
if(res == nullnode) res = capacity_;
return const_iterator(this, res);
}
// Size ----------------------------------------------------------------------------------------------------------------
[[nodiscard]] size_type capacity() const { return capacity_; }
[[nodiscard]] size_type size() const { return size_; }
[[nodiscard]] bool empty() const { return size_ == 0; }
[[nodiscard]] double occupancy() const { return size_ / static_cast(capacity_); }
// Helpers -------------------------------------------------------------------------------------------------------------
private:
void _increase_capacity();
[[nodiscard]] node _hash(const_reference v) const;
[[nodiscard]] node _find(const_reference x) const;
[[nodiscard]] node _next(node n) const { return (n + 1) % capacity_; }
[[nodiscard]] node _prev(node n) const { return (n - 1 + capacity_) % capacity_; }
static size_type _next_prime(size_type x);
static size_type _prev_prime(size_type x);
// Variables ===========================================================================================================
private:
table_type table_;
size_type capacity_, size_;
double load_factor_;
hash_type hash_;
allocator_type alloc_;
};
template
hash_table::hash_table(std::initializer_list data)
: hash_table()
{
reserve(data.size());
for(value_type val : data) { insert(val); }
}
template
hash_table::hash_table(const hash_table& other)
: table_(nullptr)
, capacity_(other.capacity_)
, size_(other.size_)
, load_factor_(0.8)
{
}
template
void hash_table::reserve(size_t size)
{
table_type old = table_;
size_type old_capacity = capacity_;
capacity_ = _next_prime(size);
table_ = alloc_.allocate(capacity_);
memset(table_, 0, capacity_ * sizeof(_Node));
size_ = 0;
for(size_type i = 0; i < old_capacity; ++i)
{
if(old[i].value()) insert(old[i].value);
}
alloc_.deallocate(old, old_capacity);
}
template
void hash_table::clear()
{
alloc_.deallocate(table_, capacity_);
capacity_ = size_ = 0;
table_ = nullptr;
}
template
void hash_table::insert(const_reference x)
{
if(occupancy() > load_factor_ || capacity_ == 0) _increase_capacity();
node idx = _hash(x);
int psl = 0;
T val = x;
while(table_[idx].value())
{
_Node& node = table_[idx];
if(*node.value == x) return;
if(psl > node.psl)
{
// std::swap(psl, node.psl); std::swap(value, node.value);
int temp_psl = psl; psl = node.psl; node.psl = temp_psl;
T temp_val = std::move(node.value); node.value = std::move(val); val = std::move(temp_val);
}
idx = _next(idx);
++psl;
}
std::construct_at(table_ + idx, val, psl);
++size_;
}
template
void hash_table::erase(const_reference x)
{
node idx = _find(x);
if(idx == nullnode) return;
table_[idx].value.reset();
--size_;
node prev = idx; idx = _next(idx);
while(table_[idx].value() && table_[idx].psl > 0)
{
_Node &a = table_[prev], &b = table_[idx];
std::swap(a, b);
--a.psl; prev = idx; idx = _next(idx);
}
}
template
bool hash_table::contains(const_reference x)
{
return _find(x) != nullnode;
}
template
void hash_table::_increase_capacity()
{
table_type old = table_;
size_type old_capacity = capacity_;
alloc_.deallocate(table_, capacity_);
capacity_ = _next_prime(capacity_);
table_ = alloc_.allocate(capacity_);
memset(table_, 0, capacity_ * sizeof(_Node));
size_ = 0;
for(size_type i = 0; i < old_capacity; ++i)
{
if(old[i].value()) insert(old[i].value);
}
alloc_.deallocate(old, old_capacity);
}
template
typename hash_table::node hash_table::_hash(const_reference v) const
{
node x = static_cast(hash_(v));
x ^= x >> 33U;
x *= UINT64_C(0xff51afd7ed558ccd);
x ^= x >> 33U;
x *= UINT64_C(0xc4ceb9fe1a85ec53);
x ^= x >> 33U;
return x % capacity_;
}
template
typename hash_table::node hash_table::_find(const_reference x) const
{
if(capacity_ == 0) return nullnode;
node idx = _hash(x);
int psl = 0;
while(table_[idx].value())
{
_Node& node = table_[idx];
if(node.psl > psl) return nullnode;
if(*node.value == x) return idx;
idx = _next(idx); ++psl;
}
return nullnode;
}
template
typename hash_table::size_type hash_table::_next_prime(size_type x)
{
size_type n = (x + 1) / 6;
n *= 2;
while(true)
{
x = (n * 6) - 1;
if(!is_prime(x)) x = (n * 6) + 1;
if(!is_prime(x)) { ++n; continue; }
return std::max(x, 7ull);
}
}
template
typename hash_table::size_type hash_table::_prev_prime(size_type x)
{
size_type n = (x + 1) / 6;
n *= 2;
while(true)
{
x = (n * 6) - 1;
if(!is_prime(x)) x = (n * 6) + 1;
if(!is_prime(x)) { --n; continue; }
return std::max(x, 7ull);
}
}
}
#endif // OPEN_CPP_UTILS_HASH_TABLE_H