pragma once
#include<vector>
#include<array>
using std::vector;
bool readInfo(size_t data, size_t bit);
bool isPrime(int n);
int nextPrime(int n);
template <typename T>
class HopscotchHash
{
public:
explicit HopscotchHash(size_t size = 101, size_t dist = 32) :MAX_DIST(dist), currentSize(0), array(nextPrime(size)), MAX_LOAD(0.5) { MAXNUM =(1<<MAX_DIST-1)+(1<<MAX_DIST-1)-1; makeEmpty(); }
HopscotchHash(const HopscotchHash & hh) :MAX_LOAD(hh.MAX_LOAD)
{
array = hh.array;
currentSize = hh.currentSize;
MAX_DIST = hh.MAX_DIST;
MAXNUM = hh.MAXNUM;
}
void makeEmpty()
{
currentSize = 0;
for (auto & entry : array)
{
entry.isActive = false;
entry.hop = 0;
}
}
bool contains(const T & xx) const
{
return findPos(xx) != -1;
}
bool insert(const T & x)
{
if (contains(x))
return false;
if (currentSize >= array.size()*MAX_LOAD)
expand();
return insertHelper(x);
}
bool insert(T & x)
{
if (contains(x))
return false;
if (currentSize >= array.size()*MAX_LOAD)
expand();
return insertHelper(x);
}
bool remove(const T & x)
{
if (!contains(x))
return false;
return removehelper(x);
}
T & lookup(T & x)
{
return array[findPos(x)].element;
}
const T & lookup(const T & x)
{
return array[findPos(x)].element;
}
HopscotchHash & operator=(const HopscotchHash & t)
{
if (this == &t)
return *this;
array = t.array;
MAX_DIST = t.MAX_DIST;
currentSize = t.currentSize;
MAXNUM = t.MAXNUM;
return *this;
}
size_t size() const
{
return array.size();
}
private:
struct HashEntry
{
T element;
size_t hop;
bool isActive;
HashEntry() :isActive(0),hop(0) {}
explicit HashEntry(const T & ele, int hpval = 0, bool act = false) :element(ele), hop(hpval), isActive(act) {}
};
vector<HashEntry> array;
const double MAX_LOAD;
size_t currentSize;
size_t MAX_DIST;
size_t MAXNUM;
bool insertHelper(const T & xx)
{
size_t hashpos = myhash(xx);
if (array[hashpos].hop == MAXNUM)
{
return failinsert(xx);
}
size_t emptypos = linearscope(xx, hashpos);
bool changed = false;
while (distance(hashpos, emptypos) >= MAX_DIST )
{
changed = false;
for (size_t i = 1;!changed; i++)
{
if (i >= MAX_DIST)
{
return failinsert(xx);
}
size_t currentpos = movebackward(emptypos, i);
for (size_t bs = 0; bs < i&&!changed; bs++)
{
if (readInfo(array[currentpos].hop, bs))
{
size_t temppos = movefoward(currentpos, bs);
std::swap(array[emptypos].element, array[temppos].element);
array[emptypos].isActive = true;
array[temppos].isActive = false;
writetrue(array[currentpos].hop, distance(currentpos,emptypos));
writefalse(array[currentpos].hop, bs);
emptypos = temppos;
changed = true;
}
}
}
}
array[emptypos].element=xx;
array[emptypos].isActive = true;
writetrue(array[hashpos].hop, distance(hashpos, emptypos));
currentSize++;
return true;
}
bool failinsert(const T & xx)
{
expand();
return insert(xx);
}
bool failinsert(T && xx)
{
expand();
return insert(xx);
}
bool insertHelper(T && xx)
{
size_t hashpos = myhash(xx);
size_t emptypos = linearscope(xx,hashpos);
if (array[hashpos].hop == MAXNUM)
return failinsert(xx);
bool changed = false;
while (distance(hashpos, emptypos) >= MAX_DIST)
{
changed = false;
for (size_t i = 1; !changed; i++)
{
if (i >= MAX_DIST)
return failinsert(xx);
size_t currentpos = movebackward(emptypos, i);
for (size_t bs = 0; bs < i && !changed; bs++)
{
if (readInfo(array[currentpos].hop, bs))
{
size_t temppos = movefoward(currentpos, bs);
std::swap(array[emptypos].element, array[temppos].element);
array[emptypos].isActive = true;
array[temppos].isActive = false;
writetrue(array[currentpos].hop, distance(currentpos, emptypos));
writefalse(array[currentpos].hop, bs);
emptypos = temppos;
changed = true;
}
}
}
}
array[emptypos].element = xx;
array[emptypos].isActive = true;
std::swap(array[emptypos].element, xx);
writetrue(array[hashpos].hop, distance(hashpos, emptypos));
currentSize++;
return true;
}
size_t & writetrue(size_t & hop, size_t bias)
{
size_t sentinel = 1;
sentinel <<= bias;
hop|=sentinel;
return hop;
}
void writefalse(size_t & hop, size_t bias)
{
size_t sentinel = 1;
sentinel <<= bias;
sentinel = ~sentinel;
hop &= sentinel;
}
size_t distance(size_t front, size_t rear)
{
if (front <= rear)
return rear - front;
else
return array.size()-front+rear ;
}
size_t linearscope(const T & xx,size_t hashpos) const
{
size_t i = 0;
while (i < array.size())
{
size_t bs = movefoward(hashpos, i);
if (!isActive(bs))
return bs;
++i;
}
return -1;
}
bool isActive(int currentPos) const
{
return array[currentPos].isActive;
}
size_t myhash(const T & x) const
{
static std::hash<T> hf;
return hf(x) %array.size();
}
bool removehelper(const T & x)
{
size_t hashpos = myhash(x);
size_t datapos = findPos(x,hashpos);
size_t bs = distance(hashpos, datapos);
writefalse(array[hashpos].hop, bs);
array[datapos].isActive = false;
currentSize--;
return true;
}
void rehash()
{
auto oldLists = array;
// Create new double-sized, empty table
array.resize(nextPrime(2 * array.size()));
makeEmpty();
// Copy table over
currentSize = 0;
for (auto & Entry : oldLists)
if(Entry.isActive)
insert(std::move(Entry.element));
}
void expand()
{
rehash();
}
size_t movefoward(size_t pos,size_t bias) const
{
pos +=bias;
if (pos < array.size())
return pos;
else
return pos-array.size();
}
size_t movebackward(size_t pos,size_t bias) const
{
if (bias > pos)
return array.size() + pos - bias;
else
return pos - bias;
}
size_t findPos(const T & xx) const
{
size_t pos = myhash(xx);
return findPos(xx, pos);
}
size_t findPos(const T & xx,size_t hashpos) const
{
size_t CurrentPos = 0;
for (size_t i = 0; i < MAX_DIST; i++)
{
if (readInfo(array[hashpos].hop, i))
{
CurrentPos = movefoward(hashpos, i);
if (array[CurrentPos].element == xx)
return CurrentPos;
}
}
return -1;
}
};