/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
#ifndef REXY_CX_HASHMAP_HPP
#define REXY_CX_HASHMAP_HPP
#include "vector.hpp"
#include "array.hpp"
#include "algorithm.hpp"
#include "hash.hpp"
#include //CHAR_BIT
#include //size_t, ptrdiff_t
#include //pair
#include //decay
#include
namespace rexy::cx{
template
struct element{
using key_type = Key;
using value_type = Value;
Key key;
Value value;
};
template
element(Key,Value) -> element;
template>
class hashmap
{
public:
using key_type = Key;
using mapped_type = Value;
using value_type = element;
using size_type = size_t;
using difference_type = ptrdiff_t;
using hasher = Hash;
using reference = mapped_type&;
using const_reference = const mapped_type&;
using pointer = mapped_type*;
using const_pointer = const mapped_type*;
static constexpr size_type single_bucket_bit = size_type{1} << ((sizeof(size_type)*CHAR_BIT) - 1);
static constexpr size_type max_size = N;
static_assert((max_size & single_bucket_bit) == 0);
private:
array m_values; //perfect hash table
array m_g; //'salt' values for indexing into the perfect hash table
public:
constexpr hashmap(const value_type(&elements)[N]);
//no key checks. give a correct key or get a random answer :)
template>>
constexpr reference operator[](U&& u)noexcept;
template>>
constexpr const_reference operator[](U&& u)const noexcept;
};
template
constexpr hashmap::hashmap(const value_type(&elements)[N]){
array,N> buckets;
array slots_used;
size_type current_bucket = 0;
//place all keys into buckets
for(auto& element : elements){
buckets[Hash{}(element.key) % max_size].push_back(element);
}
//sort the buckets based on size, largest first
cx::quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{
return left.size() > right.size();
});
//for each bucket, try different values of 'd' to try to find a hash that doesn't collide
for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){
auto& bucket = buckets[current_bucket];
//only handle buckets containing collisions
if(bucket.size() <= 1)
break;
array pass_slots_used;
vector pass_slots;
size_type d = 1;
for(size_type i = 0;i < bucket.size();){
size_type slot = Hash{}(bucket[i].key, d) % max_size;
if(pass_slots_used[slot] || slots_used[slot]){
//slot already in use, try another value for 'd'
++d;
i = 0;
pass_slots_used.fill(false);
pass_slots.clear();
}else{
//slot is good to go
pass_slots_used[slot] = true;
pass_slots.push_back(slot);
++i;
}
}
//store the successful value of 'd' at index of the first hash for this bucket
m_g[Hash{}(bucket[0].key) % max_size] = d;
//take the value from the temporary bucket into the permanent slot
for(size_type i = 0;i < bucket.size();++i){
m_values[pass_slots[i]] = std::move(bucket[i].value);
slots_used[pass_slots[i]] = true;
}
}
//Handle remaining single value buckets
size_type next_free_slot = 0;
for(;current_bucket < buckets.size();++current_bucket){
auto& bucket = buckets[current_bucket];
if(bucket.size() == 0)
break;
for(;slots_used[next_free_slot];++next_free_slot);
m_g[Hash{}(bucket[0].key) % max_size] = (next_free_slot | single_bucket_bit);
m_values[next_free_slot] = std::move(bucket[0].value);
slots_used[next_free_slot] = true;
}
}
//no key checks. give a correct key or get a random answer :)
template
template
constexpr auto hashmap::operator[](U&& key)noexcept -> reference{
auto d = m_g[UHash{}(std::forward(key)) % max_size];
if(d & single_bucket_bit)
return m_values[d & ~single_bucket_bit];
return m_values[UHash{}(std::forward(key), d) % max_size];
}
template
template
constexpr auto hashmap::operator[](U&& key)const noexcept -> const_reference{
auto d = m_g[UHash{}(std::forward(key)) % max_size];
if(d & single_bucket_bit)
return m_values[d & ~single_bucket_bit];
return m_values[UHash{}(std::forward(key), d) % max_size];
}
template>
constexpr auto make_hashmap(const typename hashmap::value_type(&list)[N]){
return hashmap(list);
}
}
#ifdef REXY_STRING_BASE_HPP
#include "string_hash.hpp"
#endif
#endif