Skip to content

Commit

Permalink
Written insert(), remove(), lookup() for HopScotchHashTable.
Browse files Browse the repository at this point in the history
Tested pushToRight()

Todo: test insert(), remove(), lookup()

Signed-off-by: saad0105050 <saad0105050@gmail.com>
  • Loading branch information
saad0105050 committed Sep 17, 2014
1 parent 8d04e32 commit e8148a7
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 113 deletions.
2 changes: 1 addition & 1 deletion YASI_12/YASI_12.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
<Filter>Header Files\Dictionary</Filter>
</ClInclude>
<ClInclude Include="ds.HopScotchHashTable.h">
<Filter>Header Files</Filter>
<Filter>Header Files\Dictionary</Filter>
</ClInclude>
</ItemGroup>
</Project>
305 changes: 288 additions & 17 deletions YASI_12/ds.HopScotchHashTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,45 @@ using namespace std;

template<class Value>
struct HopScotchEntry{
typedef HopScotchEntry<Value> self;
size_t key;
Value value;
int bitField; // map of bucket elements
HopScotchEntry() : key(0), bitField(0){}
HopScotchEntry(const size_t& k) : key(k){}
HopScotchEntry(const size_t& k, const value& v) : key(k), value(v){}
HopScotchEntry(const size_t& k, const value& v, const int& bf) :
key(k), value(v),bitField(bf){}
HopScotchEntry(const size_t& k, const Value& v) : key(k), value(v){}
HopScotchEntry(const size_t& k, const Value& v, const int& bf) :
key(k), value(v), bitField(bf){}
HopScotchEntry(const self& other):key(other.key), value(other.value),bitField(other.bitField){ }
self& operator=(const self& other){
key = other.key; value = other.value; bitField = other.bitField;
return *this;
}

};

// override for printing
template<class Value>
std::ostream& operator<< (std::ostream& out, const HopScotchEntry<Value>& kv) {
out << kv.key;
return out;
}
template<class Value>
std::ostream& operator<< (std::ostream& out, const HopScotchEntry<Value>* pKv) {
out << pKv->key;
return out;
}


// a hash table with integer key
template<
class Value = size_t,
class HashFunction = IntHashFunction<int> >
class HashFunction = IntHashFunction<int>,
class EntryType = HopScotchEntry<Value > // must have fields `key', `value', and `bitField'
>
class HopScotchHashTable
: public IntLinearProbingHashTable <
Value, HashFunction, HopScotchEntry<Value> > {
Value, HashFunction, EntryType > {

///////////////// enable testing ///////////////
friend class HopScotchHashTableTest;
Expand All @@ -33,41 +55,189 @@ class HopScotchHashTable

public:
typedef size_t Key;
typedef KVPair<Key, Value> EntryType; // must have a public member `key'
typedef EntryType BucketType; // each bucket holds an EntryType object
typedef EntryType Pair; // which is actually a key-value pair
typedef EntryType* BucketEntryPtr; // ptr to an entry object
typedef Key KeyType;
typedef Value ValueType;

protected:
typedef IntLinearProbingHashTable < Value, HashFunction >
typedef IntLinearProbingHashTable < Value, HashFunction, HopScotchEntry<Value> >
base;


virtual void copyTable(BucketType* oldTable, const unsigned int oldSize) override{
h::copyTable(oldTable, oldSize);
base::copyTable(oldTable, oldSize);
}

// swap two bucket entries
void swap(const int bucketOne, const int bucketTwo) const{
EntryType temp = table[bucketOne];
table[bucketOne] = table[bucketTwo];
table[bucketTwo] = temp;
}


//---------- HopScotch Properties/Methods ----------
static unsigned int INIT_H = 32;
unsigned size_t H; // the neighborhood size
static const unsigned int INIT_H = 4;
size_t H; // the neighborhood size
size_t _zeroKeyBucket; // the bucket containing (hashed) zero key
// we should find something within first H-1 trials
// if not found, returns NULL
BucketType* lookupRight(const int bucket, const Key& key){

}
#if YASI_ALLOW_ZERO_KEY
virtual inline bool isNull(const int bucket) const{
if (table[bucket].key != 0){
// key non-zero
return false;
}
// key is zero
else if (_zeroUsed == false){
// zero element not used
return true;
}
else{
// zero element used
if (bucket == _zeroKeyBucket)
// this is the zeroElement, not NULL
return false;
else
return true;
}
}
#else
virtual inline bool isNull(const int bucket) const{
return table[bucket].key == 0;
}
#endif // YASI_ALLOW_ZERO_KEY

// push this entry to the right
bool pushToRight(const int bucket){}
// fill this bucket with an entry from the left
void pullFromLeft(const int bucket){}
// returns false if it is not possible to push
// returns true on success
bool pushToRight(const int bucket, int startOfBumping = -1){
if (startOfBumping > 0 ){
// .... bucket startOfBumping ....
if (circularDiff(bucket, startOfBumping) == 1) {
// we traversed the entire array
// this operation is impossible
return false;
}
else{
// continue working below
}
}
else{
// initialize search
startOfBumping = bucket;
}

bool found = false;
int cur = bucket;

// try at most H-1 places to the right
int firstBucket = index(table[bucket].key);
// this is the invariant of hopscotch hashing
if (circularDiff(firstBucket, bucket) >= H){
// the table is broken
cerr << "HopScotch invariant broken: item at bucket [" << bucket << "] is originally hashed at bucket [" << firstBucket << "], which is >= H, which is " << H << endl;
return false;
}

int last = modSize(firstBucket + H-1);
if (bucket == last){
// this bucket is not allowed to leave the neighborhood
return false;
}
int next;
for (; cur != last; cur = next){
next = circularNext(cur);
if (next == startOfBumping) {
// tried all slots
// no way we can bump any more
return false;
}

if ( isNull(next) ){
// insert into blank slot
table[next] = table[bucket];
return true;
}
}
// could not place the entry within neighborhood H
// bump the last guy in the neighborhood
// make room
if (pushToRight(last, startOfBumping)){
table[last] = table[bucket];
return true;
}
else{
// the hopping failed
return false;
}
}

virtual BucketType* insertKey(const Key& k){
int firstBucket = index(k);
int cur = firstBucket;
int end = modSize(firstBucket + H);
while (!isNull(cur) && cur != end){
cur = circularNext(cur);
}
if (cur == end){
// could not insert within neighborhood
// bump the last guy
if (pushToRight( circularPrev(end) )){
table[end].key = k;
_population++;
return &table[end];
}
else{
// could not insert
// need to expand table
return NULL;
}
}
else{
// cur must be NULL
table[cur].key = k;
_population++;
return &table[end];
}
}

virtual BucketType* lookupKey(const Key& k){
int firstBucket = index(k);
for (int i = 0; i < H; i++){
if (table[firstBucket + i].key == k)
return &table[firstBucket + i];
}
return NULL;
}

virtual void removeKey(const Key& k){
BucketType* pBucket = lookupKey(k);
if (pBucket){
pBucket->key = 0;
pBucket->bitField = 0;
_population--;
}
}

public:

virtual ~HopScotchHashTable(){}
HopScotchHashTable(unsigned int logSize = INIT_LOGSIZE, unsigned int H = INIT_H) :H(H),IntLinearProbingHashTable(logSize){}
HopScotchHashTable(unsigned int logSize = INIT_LOGSIZE, unsigned int H = INIT_H) :H(H),IntLinearProbingHashTable(logSize){
assert(_size >= H);
}

};

////////// test IntLinearProbingHashTable
// inherit tests from base class, just redefine the types

class HopScotchHashTableTest
class HopScotchHashTableTest : public yasi::Test
// : public LinearProbingHashTableTestBase<
// HopScotchHashTable<int, Mod8Function<int> >,
// HopScotchHashTable<int, Mod16Function<int> >,
Expand All @@ -87,11 +257,112 @@ class HopScotchHashTableTest
public:
// inherit all public tests
void pushToRight(){
IntHashTable h(4, 4); // size 16, H=4
h.table[2] = HopScotchEntry<int>( 2, 2, 0 );
IntHashTable h(3, 4); // size 8, H=4
ASSERT_EQ(8, h.size()) << "size not 8";
ASSERT_EQ(4, h.H) << "H not 4";
for (int i = 0; i < h.size(); i++){
ASSERT_EQ(0, h.table[i].key) << "key[" << i << "] not zero";
}

h.table[2] = BucketType(2, 2, 0);
/// - - 2 - - - - -
/// 0 1 2 3 4 5 6 7

bool ok;
int b, k;
{
SCOPED_TRACE("no bumps");
/// - - 2 - - - - -
/// 0 1 2 3 4 5 6 7
ok = h.pushToRight(2); h.makeNull(2);
/// - - - 2 - - - -
/// 0 1 2 3 4 5 6 7
ASSERT_EQ(true, ok) << "push[2] failed";
// [2] will be 2 because
b = 2; k = 0; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 3; k = 2; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
// put it back
/// - - 2 - - - - -
/// 0 1 2 3 4 5 6 7
h.swap(2, 3);
b = 2; k = 2; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 3; k = 0; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;

h.table[3] = BucketType(3, 3, 0);
h.table[4] = BucketType(4, 4, 0);
h.table[5] = BucketType(5, 5, 0);
/// - - 2 3 4 5 - -
/// 0 1 2 3 4 5 6 7
ok = h.pushToRight(3); h.makeNull(3);
/// - - 2 - 4 5 3 -
/// 0 1 2 3 4 5 6 7
ASSERT_EQ(true, ok) << "push[3] failed";
b = 3; k = 0; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 6; k = 3; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
}
{
SCOPED_TRACE("one bump");
h.table[7] = BucketType(7, 7, 0);
/// - - 2 - 4 5 3 7
/// 0 1 2 3 4 5 6 7
// cout << "before push(4)" << h;
ok = h.pushToRight(4); h.makeNull(4);
//cout << "after push(4)" << h;

/// 7 - 2 - - 5 3 4
/// 0 1 2 3 4 5 6 7
ASSERT_EQ(true, ok) << "push[4] failed";
b = 4; k = 0; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 7; k = 4; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 0; k = 7; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
}
{
SCOPED_TRACE("two bumps");
h.table[1] = BucketType(6, 7, 0);
h.table[3] = BucketType(1, 7, 0);
b = 1; k = 6; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 3; k = 1; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
/// 7 6 2 1 - 5 3 4
/// 0 1 2 3 4 5 6 7
ok = h.pushToRight(5); h.makeNull(5);
/// 5 6 7 1 2 - 3 4
/// 0 1 2 3 4 5 6 7
ASSERT_EQ(true, ok) << "push[5] failed";
b = 5; k = 0; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 0; k = 5; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 1; k = 6; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 2; k = 7; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 3; k = 1; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
b = 4; k = 2; ASSERT_EQ(k, h.table[b].key) << "[" << b << "] not " << k;
}
{
SCOPED_TRACE("no more space");
h.table[5] = BucketType(17); // just fill the place with something
/// 5 6 7 1 2 17 3 4
/// 0 1 2 3 4 5 6 7
ok = h.pushToRight(1);
ASSERT_EQ(false, ok) << "push[1] succeeded";

}
}

void pushToRightOverZeroKey(){

}
void circularDiff(){
IntHashTable8 h(3,4);
ASSERT_EQ(8, h.size());
ASSERT_EQ(0, h.circularDiff(4, 4));
ASSERT_EQ(1, h.circularDiff(4, 5));
ASSERT_EQ(3, h.circularDiff(4, 7));
ASSERT_EQ(4, h.circularDiff(4, 0));
ASSERT_EQ(6, h.circularDiff(4, 2));
ASSERT_EQ(7, h.circularDiff(4, 3));
}

};
ADD_TEST_F(HopScotchHashTableTest, circularDiff);
ADD_TEST_F(HopScotchHashTableTest, pushToRight);
//ADD_TEST_F(HopScotchHashTableTest, insert);
//ADD_TEST_F(HopScotchHashTableTest, copyTable);
//ADD_TEST_F(HopScotchHashTableTest, growCondition);
Expand Down
4 changes: 2 additions & 2 deletions YASI_12/ds.IntLinearProbingHashTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ namespace yasi{

public:
typedef size_t Key;
typedef KVPair<Key, Value> EntryType; // must have a public member `key'
//typedef KVPair<Key, Value> EntryType; // must have a public member `key'
typedef EntryType BucketType; // each bucket holds an EntryType object
typedef EntryType Pair; // which is actually a key-value pair
typedef EntryType* BucketEntryPtr; // ptr to an entry object
typedef Key KeyType;
typedef Value ValueType;

protected:
typedef LinearProbingHashTable < size_t, Value, KVPairSimple<int, Value>, HashFunction >
typedef LinearProbingHashTable < size_t, Value, HashFunction, BucketType >
base;


Expand Down
Loading

0 comments on commit e8148a7

Please sign in to comment.