diff --git a/YASI_12/YASI_12.vcxproj b/YASI_12/YASI_12.vcxproj
index aa309cc..3d9ae4b 100644
--- a/YASI_12/YASI_12.vcxproj
+++ b/YASI_12/YASI_12.vcxproj
@@ -93,13 +93,16 @@
+
+
false
+
@@ -109,6 +112,7 @@
+
diff --git a/YASI_12/YASI_12.vcxproj.filters b/YASI_12/YASI_12.vcxproj.filters
index c8be0f4..ccbf8b5 100644
--- a/YASI_12/YASI_12.vcxproj.filters
+++ b/YASI_12/YASI_12.vcxproj.filters
@@ -188,5 +188,17 @@
Header Files\Data Structures\Dictionary
+
+ Header Files\Data Structures\Hash Table
+
+
+ Header Files\Data Structures\Hash Table
+
+
+ Header Files\Data Structures\Hash Table
+
+
+ Header Files\Data Structures\Hash Table
+
\ No newline at end of file
diff --git a/YASI_12/ds.HopScotchHashTable.h b/YASI_12/ds.HopScotchHashTable.h
index dfdec01..faf3f1b 100644
--- a/YASI_12/ds.HopScotchHashTable.h
+++ b/YASI_12/ds.HopScotchHashTable.h
@@ -3,9 +3,12 @@
#include "ds.IntLinearProbingHashTable.h"
#include
+// enable-disable testing classes in this file
+#include "test.this.module.h"
+using namespace std;
+
namespace yasi{
namespace ds{
-using namespace std;
template
struct HopScotchEntry{
@@ -50,9 +53,10 @@ class HopScotchHashTable
Value, HashFunction, EntryType > {
///////////////// enable testing ///////////////
- friend class HopScotchHashTableTest;
+ FRIEND_TEST_CLASS( HopScotchHashTableTest);
+
template
- friend class LinearProbingHashTableTestBase;
+ FRIEND_TEST_CLASS( LinearProbingHashTableTestBase);
public:
typedef size_t Key;
@@ -424,635 +428,6 @@ class HopScotchHashTable
};
-////////// test IntLinearProbingHashTable
-// inherit tests from base class, just redefine the types
-
-class HopScotchHashTableTest : public yasi::Test
-// : public LinearProbingHashTableTestBase<
-// HopScotchHashTable >,
-// HopScotchHashTable >,
-// HopScotchHashTable >
-//>
-{
- //
-protected:
- typedef HopScotchHashTable > IntHashTable8;
- typedef HopScotchHashTable > IntHashTable16;
- typedef IntHashTable16 IntHashTable; // default
- typedef HopScotchHashTable > IntHashTable32;
- typedef IntHashTable::BucketType BucketType;
- typedef IntHashTable::BucketEntryPtr BucketEntryPtr;
- typedef IntHashTable::Pair Pair;
-
-public:
- // inherit all public tests
-
- //void pushToRight(){
- // 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";
-
- // }
- //}
-
- // tests whether it is ok to move emptySlot to cur for a given firstBucket
- // hash(cur) should lie between firstBucket and emptySlot, and
- // hash(cur) should be withing H-1 of emptySlot
- void canSwapCurWithEmpty(){
- IntHashTable h(4, 4); // size 16, H=4
- int f, c, hc, e;
- {
- string str = "non-circular. boundary involving f,c,e";
- SCOPED_TRACE(str);
- f = 4, c = 5, hc = 5, e = 4;
-
- // f == e
- f = e;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
- f = 4;
-
- // f == c
- c = f;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // c < f
- c = f - 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // c = e
- c = e;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // c > e
- c = e + 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
- }
- {
- string str = "circular. boundary involving f,c,e";
- SCOPED_TRACE(str);
- f = 14, c = 14, hc = 15, e = 1;
-
- // f == c
- c = f;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // c < f
- c = f - 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // c == e
- c = e;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // c > e
- c = e + 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
- }
-
- {
- string str = "non-circular. hash(cur) not in range";
- SCOPED_TRACE(str);
- f = 4, c = 5, e = 8;
-
- // hc < f
- hc = f - 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc == f
- hc = f;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc == e
- hc = e;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc > e
- hc = e + 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc < e - H
- hc = e - h.H - 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc == e - H
- hc = e - h.H;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- }
- {
- string str = "circular. hash(cur) not in range";
- SCOPED_TRACE(str);
- f = 12, c = 2, e = 3;
-
- // hc < f
- hc = f - 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc == f
- hc = 4;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc == e
- hc = e;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc > e
- hc = e + 1;
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc < e - H
- hc = h.modSize(e - h.H - 1);
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // hc == e - H
- hc = h.modSize(e - h.H);
- ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- }
- {
- string str = "hash(cur) in range";
- SCOPED_TRACE(str);
-
- // non-circular
- f = 4, c = 8, e = 10;
-
- // hc > e-H
- hc = e - h.H + 1;
- ASSERT_EQ(true, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
-
- // circular
- f = 11, c = 15, e = 2;
-
- // hc > e-H
- hc = h.modSize(e - h.H + 1);
- ASSERT_EQ(true, h.canSwapCurWithEmpty(f, c, hc, e))
- << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
- }
-
- }
-
- void pullFromLeft(){
- IntHashTable h(4, 4); // size 16, H=4
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- int k, b, f;
- BucketType* p;
-
- // test scenarios:
- // - empty slot is within H-1 of firstBucket
- // - the replacement item j is found, which is within H-1 of firstBucket
- // - the replacement item j is found, which is not within H-1 of firstBucket
- // - the replacement item j could not be found within H-1 of emptySlot
- // - above, circular
- {
- SCOPED_TRACE("case #1");
- h.insert(4, 4);
- h.insert(5, 5);
- h.insert(6, 6);
- ASSERT_EQ(3, h.population());
-
- // 20 is hashed in 4, should be placed in 7
- k = 20; b = 7;
- ASSERT_EQ(true, h.isNull(b));
- p = h.pullFromLeft(4, b);
- ASSERT_NE(NULL, (int)p);
- ASSERT_EQ(&h.table[b], p);
- h.table[b] = BucketType(k, k, 0);
-
- // circular
- h.clear();
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- h.insert(14, 14);
- h.insert(15, 15);
- h.insert(31, 31); // hash value 15
- // 17 is hashed in 4, should be placed in 7
- k = 17; b = 1;
- ASSERT_EQ(true, h.isNull(b));
- p = h.pullFromLeft(14, b);
- ASSERT_NE(NULL, (int)p); // already within H
- ASSERT_EQ(&h.table[b], p);
-
- }
- {
- SCOPED_TRACE("the replacement item j is found, which is within H-1 of firstBucket");
- h.clear();
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- h.table[3] = BucketType(3);
- h.table[4] = BucketType(4);
- h.table[5] = BucketType(21); // hash value = 6
- h.table[6] = BucketType(22); // hash value = 5
- h.table[7] = BucketType(19); // hash value = 3
-
- cout << "before pulling 8\n" << h;
- p = h.pullFromLeft(3, 8); // empty slot should go to 5
- cout << "after pulling 8\n" << h;
- ASSERT_EQ(&h.table[5], p);
- ASSERT_EQ(3, h.table[3].key);
- ASSERT_EQ(4, h.table[4].key);
- ASSERT_EQ(0, h.table[5].key);
- ASSERT_EQ(22, h.table[6].key);
- ASSERT_EQ(19, h.table[7].key);
- ASSERT_EQ(21, h.table[8].key);
-
- // now try a case where key hashes prohibit their move
- h.clear();
- h.table[3] = BucketType(3);
- h.table[4] = BucketType(4);
- h.table[5] = BucketType(18); // hash value = 2
- h.table[6] = BucketType(17); // hash value = 1
- h.table[7] = BucketType(21); // hash value = 5
-
- cout << "before pulling 8\n" << h;
- p = h.pullFromLeft(3, 8); // empty slot should go to 4
- cout << "after pulling 8\n" << h;
- ASSERT_EQ(&h.table[4], p);
- ASSERT_EQ(3, h.table[3].key);
- ASSERT_EQ(0, h.table[4].key);
- ASSERT_EQ(18, h.table[5].key);
- ASSERT_EQ(17, h.table[6].key);
- ASSERT_EQ(4, h.table[7].key);
- ASSERT_EQ(21, h.table[8].key);
-
- /// circular
- h.clear();
- cout << "after clear\n" << h;
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- k = 14; h.table[k] = BucketType(k);
- k = 15; h.table[k] = BucketType(k);
- k = 0; h.table[k] = BucketType(159); // hash value 15
- k = 1; h.table[k] = BucketType(30); // hash value 14
- k = 2; h.table[k] = BucketType(k);
- k = 3; h.table[k] = BucketType(k);
- // for firstBucket 15, empty slot [4] should move to [2]
- // because [1]'s hash value is 14
- b = 2; f = 15;
- cout << "before moving from [4]\n" << h;
- ASSERT_EQ(true, h.isNull(4));
- p = h.pullFromLeft(f, 4);
- cout << "after moving from [4]\n" << h;
- ASSERT_EQ(&h.table[2], p);
- ASSERT_EQ(14, h.table[14].key);
- ASSERT_EQ(15, h.table[15].key);
- ASSERT_EQ(159, h.table[0].key);
- ASSERT_EQ(30, h.table[1].key);
- ASSERT_EQ(true, h.isNull(2));
- ASSERT_EQ(3, h.table[3].key);
- ASSERT_EQ(2, h.table[4].key);
- }
- {
- SCOPED_TRACE("the replacement item j is found, which is NOT within H-1 of firstBucket");
- h.clear();
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- h.table[0] = BucketType(31); // hash to 15
- h.table[1] = BucketType(1);
- h.table[2] = BucketType(17); // hash 1
- h.table[3] = BucketType(33); // hash 1
- for (int i = 4; i < 16; i++){
- h.table[i] = BucketType(i);
- }
- h.table[13] = BucketType(0);
- /// keys: 31 1 17 33 4 5 6 7 8 9 10 11 12 0 14 15
- /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << "before moving from [13]\n" << h;
- p = h.pullFromLeft(1, 13); // empty slot should go to 4
- cout << "after moving from [13]\n" << h;
- /// keys: 31 1 17 33 0 5 6 4 8 9 7 11 12 10 14 15
- /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- for (int i = 1; i < 16; i++){
- if (i == 4)
- ASSERT_EQ(true, h.isNull(i));
- else if (i == 13)
- ASSERT_EQ(10, h.table[i].key);
- else if (i == 10)
- ASSERT_EQ(7, h.table[i].key);
- else if (i == 7)
- ASSERT_EQ(4, h.table[i].key);
- else if (i == 2)
- ASSERT_EQ(17, h.table[i].key);
- else if (i == 3)
- ASSERT_EQ(33, h.table[i].key);
- else
- ASSERT_EQ(i, h.table[i].key);
- }
-
- /// circular
- h.clear();
- f = 4;
-
- for (int i = 0; i < 16; i++) h.table[i] = BucketType(i);
- h.table[3].key = 0; // make [3] null
- h.table[0].key = 0; // make [0] null
- h.table[15].key = 17; // make [15].key hash to 1, cannot move
- h.table[13].key = 0; // make [13] null
- h.table[10].key = 25; // make [10].key hash to 25
- h.table[11].key = 0; // make [11] null
- h.table[9].key = 19; // make [9].key hash to 3, cannot move
- h.table[5].key = 2; // make [5].key hash to 2, cannot move
-
- /// keys: 0 1 2 0 4 2 6 7 8 19 25 0 12 0 14 17
- /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << "before moving from [3]\n" << h;
- p = h.pullFromLeft(4, 3); // empty slot should settle at [7]
- cout << "after moving from [3]\n" << h;
- /// keys: 0 14 2 1 4 2 6 0 8 19 7 0 25 0 12 17
- /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-
- ASSERT_EQ(&h.table[7], p);
- ASSERT_EQ(1, h.table[3].key); // first swap with 1
- ASSERT_EQ(14, h.table[1].key); // then swap with 14
- ASSERT_EQ(12, h.table[14].key); // then swap with 12
- ASSERT_EQ(25, h.table[12].key); // then swap with 10
- ASSERT_EQ(7, h.table[10].key); // then swap with 7
- ASSERT_EQ(0, h.table[7].key); // 7 is now empty slot
- }
- {
- SCOPED_TRACE("the replacement item j could not be found within H-1 of emptySlot");
- h.clear();
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- for (int i = 0; i < 15; i++){
- h.table[i] = BucketType(i * 16 + 2 ); // all hash to 2
- }
-
- p = h.pullFromLeft(3, 9); // should fail
- ASSERT_EQ(NULL, (int)p);
- p = h.pullFromLeft(8, 2); // should fail
- ASSERT_EQ(NULL, (int)p);
- }
- }
-
- 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));
- }
-
- void insertRemoveLookup(){
- IntHashTable h(4, 4); // size 16, H=4
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(4, h.H);
-
- // try to find an empty key
- ASSERT_EQ(NULL, (int)h.lookupKey(7));
- ASSERT_EQ(NULL, (int)h.lookupKey(0));
-
- // insert one key, find it, and remove it
- h.put(5, 50);
- ASSERT_EQ(true, h.contains(5));
- ASSERT_EQ(5, h.lookupKey(5)->key);
- ASSERT_EQ(50, *h.get(5));
- ASSERT_EQ(1, h.population());
- ASSERT_EQ(16, h.size());
- h.remove(5);
- ASSERT_EQ(false, h.contains(5));
- ASSERT_EQ(NULL, (int)h.lookupKey(5));
- ASSERT_EQ(NULL, (int)h.get(5));
- ASSERT_EQ(0, h.population());
- ASSERT_EQ(16, h.size());
-
- // insert many keys that should not cause resize
- h.clear();
- ASSERT_EQ(16, h.size());
- int numItemsWithoutResize = h.maxPopulationWithoutGrow();
- for (int i = 0; i < numItemsWithoutResize; i++){
- h.insert(16+i, 16+i);
- }
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(numItemsWithoutResize, h.population());
-
- // test remove
- ASSERT_EQ(true, h.contains(21));
- h.remove(21);
- ASSERT_EQ(false, h.contains(21));
- ASSERT_EQ(16, h.size());
- ASSERT_EQ(numItemsWithoutResize - 1, h.population());
- // put it back
- h.insert(21, 21);
-
- // now add one more item, should cause grow()
- h.insert(15, 15);
- ASSERT_EQ(true, h.contains(15));
- ASSERT_EQ(32, h.size());
- ASSERT_EQ(numItemsWithoutResize + 1, h.population());
-
- ///////// test resizing when a bucket gets more than H items
- IntHashTable32 h2(4, 4); // keep the hash func mod 32 so that we can grow and rehash
- for (int i = 0; i < h2.H; i++){
- h2.insert(16 * i + 2, i); // all hashed to 2
- }
- ASSERT_EQ(16, h2.size());
- ASSERT_EQ(h2.H, h2.population());
- // this insert should cause grow
- cout << "before inserting " << 16 * h2.H + 2 << endl << h2;
- h2.insert(16 * h2.H + 2, h2.H);
- cout << "after inserting " << 16 * h2.H + 2 << endl << h2;
- ASSERT_EQ(32, h2.size());
- ASSERT_EQ(h2.H+1, h2.population());
- }
- void nextPosInBucket(){
- IntHashTable h(4, 4);
- int cur, bitmap, next;
-
- bitmap = 0xC0000000; cur = -1; next = -1;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = h.H; next = -1;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = h.H - 1; next = -1;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = 1; next = -1;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = 0; next = 1;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC8800000; cur = 4; next = -1; // cur >= H
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC8000000; cur = 1; next = 4;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000001; cur = 1; next = 31;
- ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
- << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
- }
-
- void prevPosInBucket(){
- IntHashTable h(4, 4);
- int cur, bitmap, prev;
-
- bitmap = 0xC0000000; cur = -1; prev = -1;
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = 0; prev = -1;
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = h.H; prev = -1;
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC0000000; cur = 1; prev = 0;
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xA0000000; cur = 2; prev = 0;
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xC8000000; cur = 4; prev = -1; // cur >= H
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
-
- bitmap = 0xD8000000; cur = 3; prev = 1;
- ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
- << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
- }
-
-};
-ADD_TEST_F(HopScotchHashTableTest, circularDiff);
-ADD_TEST_F(HopScotchHashTableTest, nextPosInBucket);
-ADD_TEST_F(HopScotchHashTableTest, prevPosInBucket);
-ADD_TEST_F(HopScotchHashTableTest, canSwapCurWithEmpty);
-ADD_TEST_F(HopScotchHashTableTest, pullFromLeft);
-ADD_TEST_F(HopScotchHashTableTest, insertRemoveLookup);
-
}
}
\ No newline at end of file
diff --git a/YASI_12/ds.HopScotchHashTable.test.h b/YASI_12/ds.HopScotchHashTable.test.h
new file mode 100644
index 0000000..765b98a
--- /dev/null
+++ b/YASI_12/ds.HopScotchHashTable.test.h
@@ -0,0 +1,640 @@
+#pragma once
+
+#if YASI_TEST_THIS_MODULE != 1
+#define YASI_TEST_THIS_MODULE 1
+#endif
+#include "ds.HopScotchHashTable.h"
+#include "ds.LinearProbingHashTable.test.h"
+
+namespace yasi{
+ namespace ds{
+ ////////// test HopScotchHashTable
+ // inherit tests from base class, just redefine the types
+
+ class HopScotchHashTableTest : public yasi::Test
+ // : public LinearProbingHashTableTestBase<
+ // HopScotchHashTable >,
+ // HopScotchHashTable >,
+ // HopScotchHashTable >
+ //>
+ {
+ //
+ protected:
+ typedef HopScotchHashTable > IntHashTable8;
+ typedef HopScotchHashTable > IntHashTable16;
+ typedef IntHashTable16 IntHashTable; // default
+ typedef HopScotchHashTable > IntHashTable32;
+ typedef IntHashTable::BucketType BucketType;
+ typedef IntHashTable::BucketEntryPtr BucketEntryPtr;
+ typedef IntHashTable::Pair Pair;
+
+ public:
+ // inherit all public tests
+
+ //void pushToRight(){
+ // 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";
+
+ // }
+ //}
+
+ // tests whether it is ok to move emptySlot to cur for a given firstBucket
+ // hash(cur) should lie between firstBucket and emptySlot, and
+ // hash(cur) should be withing H-1 of emptySlot
+ void canSwapCurWithEmpty(){
+ IntHashTable h(4, 4); // size 16, H=4
+ int f, c, hc, e;
+ {
+ string str = "non-circular. boundary involving f,c,e";
+ SCOPED_TRACE(str);
+ f = 4, c = 5, hc = 5, e = 4;
+
+ // f == e
+ f = e;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+ f = 4;
+
+ // f == c
+ c = f;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // c < f
+ c = f - 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // c = e
+ c = e;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // c > e
+ c = e + 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+ }
+ {
+ string str = "circular. boundary involving f,c,e";
+ SCOPED_TRACE(str);
+ f = 14, c = 14, hc = 15, e = 1;
+
+ // f == c
+ c = f;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // c < f
+ c = f - 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // c == e
+ c = e;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // c > e
+ c = e + 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+ }
+
+ {
+ string str = "non-circular. hash(cur) not in range";
+ SCOPED_TRACE(str);
+ f = 4, c = 5, e = 8;
+
+ // hc < f
+ hc = f - 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc == f
+ hc = f;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc == e
+ hc = e;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc > e
+ hc = e + 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc < e - H
+ hc = e - h.H - 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc == e - H
+ hc = e - h.H;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ }
+ {
+ string str = "circular. hash(cur) not in range";
+ SCOPED_TRACE(str);
+ f = 12, c = 2, e = 3;
+
+ // hc < f
+ hc = f - 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc == f
+ hc = 4;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc == e
+ hc = e;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc > e
+ hc = e + 1;
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc < e - H
+ hc = h.modSize(e - h.H - 1);
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // hc == e - H
+ hc = h.modSize(e - h.H);
+ ASSERT_EQ(false, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ }
+ {
+ string str = "hash(cur) in range";
+ SCOPED_TRACE(str);
+
+ // non-circular
+ f = 4, c = 8, e = 10;
+
+ // hc > e-H
+ hc = e - h.H + 1;
+ ASSERT_EQ(true, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+
+ // circular
+ f = 11, c = 15, e = 2;
+
+ // hc > e-H
+ hc = h.modSize(e - h.H + 1);
+ ASSERT_EQ(true, h.canSwapCurWithEmpty(f, c, hc, e))
+ << "canSwapCurWithEmpty(" << f << ", " << c << ", " << hc << ", " << e << ")";
+ }
+
+ }
+
+ void pullFromLeft(){
+ IntHashTable h(4, 4); // size 16, H=4
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ int k, b, f;
+ BucketType* p;
+
+ // test scenarios:
+ // - empty slot is within H-1 of firstBucket
+ // - the replacement item j is found, which is within H-1 of firstBucket
+ // - the replacement item j is found, which is not within H-1 of firstBucket
+ // - the replacement item j could not be found within H-1 of emptySlot
+ // - above, circular
+ {
+ SCOPED_TRACE("case #1");
+ h.insert(4, 4);
+ h.insert(5, 5);
+ h.insert(6, 6);
+ ASSERT_EQ(3, h.population());
+
+ // 20 is hashed in 4, should be placed in 7
+ k = 20; b = 7;
+ ASSERT_EQ(true, h.isNull(b));
+ p = h.pullFromLeft(4, b);
+ ASSERT_NE(NULL, (int)p);
+ ASSERT_EQ(&h.table[b], p);
+ h.table[b] = BucketType(k, k, 0);
+
+ // circular
+ h.clear();
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ h.insert(14, 14);
+ h.insert(15, 15);
+ h.insert(31, 31); // hash value 15
+ // 17 is hashed in 4, should be placed in 7
+ k = 17; b = 1;
+ ASSERT_EQ(true, h.isNull(b));
+ p = h.pullFromLeft(14, b);
+ ASSERT_NE(NULL, (int)p); // already within H
+ ASSERT_EQ(&h.table[b], p);
+
+ }
+ {
+ SCOPED_TRACE("the replacement item j is found, which is within H-1 of firstBucket");
+ h.clear();
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ h.table[3] = BucketType(3);
+ h.table[4] = BucketType(4);
+ h.table[5] = BucketType(21); // hash value = 6
+ h.table[6] = BucketType(22); // hash value = 5
+ h.table[7] = BucketType(19); // hash value = 3
+
+ cout << "before pulling 8\n" << h;
+ p = h.pullFromLeft(3, 8); // empty slot should go to 5
+ cout << "after pulling 8\n" << h;
+ ASSERT_EQ(&h.table[5], p);
+ ASSERT_EQ(3, h.table[3].key);
+ ASSERT_EQ(4, h.table[4].key);
+ ASSERT_EQ(0, h.table[5].key);
+ ASSERT_EQ(22, h.table[6].key);
+ ASSERT_EQ(19, h.table[7].key);
+ ASSERT_EQ(21, h.table[8].key);
+
+ // now try a case where key hashes prohibit their move
+ h.clear();
+ h.table[3] = BucketType(3);
+ h.table[4] = BucketType(4);
+ h.table[5] = BucketType(18); // hash value = 2
+ h.table[6] = BucketType(17); // hash value = 1
+ h.table[7] = BucketType(21); // hash value = 5
+
+ cout << "before pulling 8\n" << h;
+ p = h.pullFromLeft(3, 8); // empty slot should go to 4
+ cout << "after pulling 8\n" << h;
+ ASSERT_EQ(&h.table[4], p);
+ ASSERT_EQ(3, h.table[3].key);
+ ASSERT_EQ(0, h.table[4].key);
+ ASSERT_EQ(18, h.table[5].key);
+ ASSERT_EQ(17, h.table[6].key);
+ ASSERT_EQ(4, h.table[7].key);
+ ASSERT_EQ(21, h.table[8].key);
+
+ /// circular
+ h.clear();
+ cout << "after clear\n" << h;
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ k = 14; h.table[k] = BucketType(k);
+ k = 15; h.table[k] = BucketType(k);
+ k = 0; h.table[k] = BucketType(159); // hash value 15
+ k = 1; h.table[k] = BucketType(30); // hash value 14
+ k = 2; h.table[k] = BucketType(k);
+ k = 3; h.table[k] = BucketType(k);
+ // for firstBucket 15, empty slot [4] should move to [2]
+ // because [1]'s hash value is 14
+ b = 2; f = 15;
+ cout << "before moving from [4]\n" << h;
+ ASSERT_EQ(true, h.isNull(4));
+ p = h.pullFromLeft(f, 4);
+ cout << "after moving from [4]\n" << h;
+ ASSERT_EQ(&h.table[2], p);
+ ASSERT_EQ(14, h.table[14].key);
+ ASSERT_EQ(15, h.table[15].key);
+ ASSERT_EQ(159, h.table[0].key);
+ ASSERT_EQ(30, h.table[1].key);
+ ASSERT_EQ(true, h.isNull(2));
+ ASSERT_EQ(3, h.table[3].key);
+ ASSERT_EQ(2, h.table[4].key);
+ }
+ {
+ SCOPED_TRACE("the replacement item j is found, which is NOT within H-1 of firstBucket");
+ h.clear();
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ h.table[0] = BucketType(31); // hash to 15
+ h.table[1] = BucketType(1);
+ h.table[2] = BucketType(17); // hash 1
+ h.table[3] = BucketType(33); // hash 1
+ for (int i = 4; i < 16; i++){
+ h.table[i] = BucketType(i);
+ }
+ h.table[13] = BucketType(0);
+ /// keys: 31 1 17 33 4 5 6 7 8 9 10 11 12 0 14 15
+ /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << "before moving from [13]\n" << h;
+ p = h.pullFromLeft(1, 13); // empty slot should go to 4
+ cout << "after moving from [13]\n" << h;
+ /// keys: 31 1 17 33 0 5 6 4 8 9 7 11 12 10 14 15
+ /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ for (int i = 1; i < 16; i++){
+ if (i == 4)
+ ASSERT_EQ(true, h.isNull(i));
+ else if (i == 13)
+ ASSERT_EQ(10, h.table[i].key);
+ else if (i == 10)
+ ASSERT_EQ(7, h.table[i].key);
+ else if (i == 7)
+ ASSERT_EQ(4, h.table[i].key);
+ else if (i == 2)
+ ASSERT_EQ(17, h.table[i].key);
+ else if (i == 3)
+ ASSERT_EQ(33, h.table[i].key);
+ else
+ ASSERT_EQ(i, h.table[i].key);
+ }
+
+ /// circular
+ h.clear();
+ f = 4;
+
+ for (int i = 0; i < 16; i++) h.table[i] = BucketType(i);
+ h.table[3].key = 0; // make [3] null
+ h.table[0].key = 0; // make [0] null
+ h.table[15].key = 17; // make [15].key hash to 1, cannot move
+ h.table[13].key = 0; // make [13] null
+ h.table[10].key = 25; // make [10].key hash to 25
+ h.table[11].key = 0; // make [11] null
+ h.table[9].key = 19; // make [9].key hash to 3, cannot move
+ h.table[5].key = 2; // make [5].key hash to 2, cannot move
+
+ /// keys: 0 1 2 0 4 2 6 7 8 19 25 0 12 0 14 17
+ /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << "before moving from [3]\n" << h;
+ p = h.pullFromLeft(4, 3); // empty slot should settle at [7]
+ cout << "after moving from [3]\n" << h;
+ /// keys: 0 14 2 1 4 2 6 0 8 19 7 0 25 0 12 17
+ /// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+
+ ASSERT_EQ(&h.table[7], p);
+ ASSERT_EQ(1, h.table[3].key); // first swap with 1
+ ASSERT_EQ(14, h.table[1].key); // then swap with 14
+ ASSERT_EQ(12, h.table[14].key); // then swap with 12
+ ASSERT_EQ(25, h.table[12].key); // then swap with 10
+ ASSERT_EQ(7, h.table[10].key); // then swap with 7
+ ASSERT_EQ(0, h.table[7].key); // 7 is now empty slot
+ }
+ {
+ SCOPED_TRACE("the replacement item j could not be found within H-1 of emptySlot");
+ h.clear();
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ for (int i = 0; i < 15; i++){
+ h.table[i] = BucketType(i * 16 + 2); // all hash to 2
+ }
+
+ p = h.pullFromLeft(3, 9); // should fail
+ ASSERT_EQ(NULL, (int)p);
+ p = h.pullFromLeft(8, 2); // should fail
+ ASSERT_EQ(NULL, (int)p);
+ }
+ }
+
+ 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));
+ }
+
+ void insertRemoveLookup(){
+ IntHashTable h(4, 4); // size 16, H=4
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(4, h.H);
+
+ // try to find an empty key
+ ASSERT_EQ(NULL, (int)h.lookupKey(7));
+ ASSERT_EQ(NULL, (int)h.lookupKey(0));
+
+ // insert one key, find it, and remove it
+ h.put(5, 50);
+ ASSERT_EQ(true, h.contains(5));
+ ASSERT_EQ(5, h.lookupKey(5)->key);
+ ASSERT_EQ(50, *h.get(5));
+ ASSERT_EQ(1, h.population());
+ ASSERT_EQ(16, h.size());
+ h.remove(5);
+ ASSERT_EQ(false, h.contains(5));
+ ASSERT_EQ(NULL, (int)h.lookupKey(5));
+ ASSERT_EQ(NULL, (int)h.get(5));
+ ASSERT_EQ(0, h.population());
+ ASSERT_EQ(16, h.size());
+
+ // insert many keys that should not cause resize
+ h.clear();
+ ASSERT_EQ(16, h.size());
+ int numItemsWithoutResize = h.maxPopulationWithoutGrow();
+ for (int i = 0; i < numItemsWithoutResize; i++){
+ h.insert(16 + i, 16 + i);
+ }
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(numItemsWithoutResize, h.population());
+
+ // test remove
+ ASSERT_EQ(true, h.contains(21));
+ h.remove(21);
+ ASSERT_EQ(false, h.contains(21));
+ ASSERT_EQ(16, h.size());
+ ASSERT_EQ(numItemsWithoutResize - 1, h.population());
+ // put it back
+ h.insert(21, 21);
+
+ // now add one more item, should cause grow()
+ h.insert(15, 15);
+ ASSERT_EQ(true, h.contains(15));
+ ASSERT_EQ(32, h.size());
+ ASSERT_EQ(numItemsWithoutResize + 1, h.population());
+
+ ///////// test resizing when a bucket gets more than H items
+ IntHashTable32 h2(4, 4); // keep the hash func mod 32 so that we can grow and rehash
+ for (int i = 0; i < h2.H; i++){
+ h2.insert(16 * i + 2, i); // all hashed to 2
+ }
+ ASSERT_EQ(16, h2.size());
+ ASSERT_EQ(h2.H, h2.population());
+ // this insert should cause grow
+ cout << "before inserting " << 16 * h2.H + 2 << endl << h2;
+ h2.insert(16 * h2.H + 2, h2.H);
+ cout << "after inserting " << 16 * h2.H + 2 << endl << h2;
+ ASSERT_EQ(32, h2.size());
+ ASSERT_EQ(h2.H + 1, h2.population());
+ }
+ void nextPosInBucket(){
+ IntHashTable h(4, 4);
+ int cur, bitmap, next;
+
+ bitmap = 0xC0000000; cur = -1; next = -1;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = h.H; next = -1;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = h.H - 1; next = -1;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = 1; next = -1;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = 0; next = 1;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC8800000; cur = 4; next = -1; // cur >= H
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC8000000; cur = 1; next = 4;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000001; cur = 1; next = 31;
+ ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
+ << "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
+ }
+
+ void prevPosInBucket(){
+ IntHashTable h(4, 4);
+ int cur, bitmap, prev;
+
+ bitmap = 0xC0000000; cur = -1; prev = -1;
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = 0; prev = -1;
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = h.H; prev = -1;
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC0000000; cur = 1; prev = 0;
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xA0000000; cur = 2; prev = 0;
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xC8000000; cur = 4; prev = -1; // cur >= H
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+
+ bitmap = 0xD8000000; cur = 3; prev = 1;
+ ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
+ << "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
+ }
+
+ };
+ ADD_TEST_F(HopScotchHashTableTest, circularDiff);
+ ADD_TEST_F(HopScotchHashTableTest, nextPosInBucket);
+ ADD_TEST_F(HopScotchHashTableTest, prevPosInBucket);
+ ADD_TEST_F(HopScotchHashTableTest, canSwapCurWithEmpty);
+ ADD_TEST_F(HopScotchHashTableTest, pullFromLeft);
+ ADD_TEST_F(HopScotchHashTableTest, insertRemoveLookup);
+ }
+}
\ No newline at end of file
diff --git a/YASI_12/ds.IntLinearProbingHashTable.h b/YASI_12/ds.IntLinearProbingHashTable.h
index 7d0f1ff..7a15ce6 100644
--- a/YASI_12/ds.IntLinearProbingHashTable.h
+++ b/YASI_12/ds.IntLinearProbingHashTable.h
@@ -1,6 +1,10 @@
#pragma once
#include "common.h"
#include "ds.LinearProbingHashTable.h"
+
+// enable-disable testing classes in this file
+#include "test.this.module.h"
+
using namespace std;
namespace yasi{
@@ -58,33 +62,7 @@ namespace yasi{
};
- ////////// test IntLinearProbingHashTable
- // inherit tests from base class, just redefine the types
-
- class IntLinearProbingHashTableTest : public LinearProbingHashTableTestBase<
- IntLinearProbingHashTable >,
- IntLinearProbingHashTable >,
- IntLinearProbingHashTable >
- >{
- //
- protected:
- typedef IntLinearProbingHashTable > IntHashTable8;
- typedef IntLinearProbingHashTable > IntHashTable16;
- typedef IntHashTable16 IntHashTable; // default
- typedef IntLinearProbingHashTable > IntHashTable32;
- typedef IntHashTable::BucketType BucketType;
- typedef IntHashTable::BucketEntryPtr BucketEntryPtr;
- typedef IntHashTable::Pair Pair;
- public:
- // inherit all public tests
- };
- ADD_TEST_F(IntLinearProbingHashTableTest, insert);
- ADD_TEST_F(IntLinearProbingHashTableTest, copyTable);
- ADD_TEST_F(IntLinearProbingHashTableTest, growCondition);
- ADD_TEST_F(IntLinearProbingHashTableTest, shrinkCondition);
- ADD_TEST_F(IntLinearProbingHashTableTest, isKeyZero);
- ADD_TEST_F(IntLinearProbingHashTableTest, remove);
}
}
\ No newline at end of file
diff --git a/YASI_12/ds.IntLinearProbingHashTable.test.h b/YASI_12/ds.IntLinearProbingHashTable.test.h
new file mode 100644
index 0000000..e59b4ec
--- /dev/null
+++ b/YASI_12/ds.IntLinearProbingHashTable.test.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#if YASI_TEST_THIS_MODULE != 1
+#define YASI_TEST_THIS_MODULE 1
+#endif
+#include "ds.IntLinearProbingHashTable.h"
+#include "ds.LinearProbingHashTable.test.h"
+
+namespace yasi{
+ namespace ds{
+ ////////// test IntLinearProbingHashTable
+ // inherit tests from base class, just redefine the types
+
+ class IntLinearProbingHashTableTest : public LinearProbingHashTableTestBase<
+ IntLinearProbingHashTable >,
+ IntLinearProbingHashTable >,
+ IntLinearProbingHashTable >
+ >{
+ //
+ protected:
+ typedef IntLinearProbingHashTable > IntHashTable8;
+ typedef IntLinearProbingHashTable > IntHashTable16;
+ typedef IntHashTable16 IntHashTable; // default
+ typedef IntLinearProbingHashTable > IntHashTable32;
+ typedef IntHashTable::BucketType BucketType;
+ typedef IntHashTable::BucketEntryPtr BucketEntryPtr;
+ typedef IntHashTable::Pair Pair;
+ public:
+ // inherit all public tests
+
+ };
+ ADD_TEST_F(IntLinearProbingHashTableTest, insert);
+ ADD_TEST_F(IntLinearProbingHashTableTest, copyTable);
+ ADD_TEST_F(IntLinearProbingHashTableTest, growCondition);
+ ADD_TEST_F(IntLinearProbingHashTableTest, shrinkCondition);
+ ADD_TEST_F(IntLinearProbingHashTableTest, isKeyZero);
+ ADD_TEST_F(IntLinearProbingHashTableTest, remove);
+ }
+}
\ No newline at end of file
diff --git a/YASI_12/ds.LinearProbingHashTable.h b/YASI_12/ds.LinearProbingHashTable.h
index fcecaa5..4fe1f48 100644
--- a/YASI_12/ds.LinearProbingHashTable.h
+++ b/YASI_12/ds.LinearProbingHashTable.h
@@ -4,55 +4,16 @@
#include
#include
#include
+
+// enable-disable testing classes in this file
+#include "test.this.module.h"
+
using namespace std;
namespace yasi{
namespace ds{
- namespace testhash{
- template
- class IdentityFunction : public IHashFunction < Key > {
- public:
- virtual int operator()(const Key& n) const override{
- return n;
- }
- };
- template
- class Mod8Function : public IHashFunction < Key > {
- public:
- static const unsigned int modulus = 8;
- virtual int operator()(const Key& n) const override{
- return n % 8;
- }
- };
- template
- class Mod16Function : public IHashFunction < Key > {
- public:
- static const unsigned int modulus = 16;
- virtual int operator()(const Key& n) const override{
- return n % 16;
- }
- };
- template
- class Mod32Function : public IHashFunction < Key > {
- public:
- static const unsigned int modulus = 32;
- virtual int operator()(const Key& n) const override{
- return n % 32;
- }
- };
- // mod 16 by default
- template
- class ModFunction : public IHashFunction < Key > {
- public:
- unsigned int modulus;
- ModFunction() : modulus(16){}
- virtual int operator()(const Key& n) const override{
- return n % modulus;
- }
- };
- } // namespace testhash
using namespace testhash;
@@ -70,8 +31,8 @@ namespace ds{
HashFunction > {
///////////////// enable testing ///////////////////
template
- friend class LinearProbingHashTableTestBase;
- friend class LinearProbingHashTableTest;
+ FRIEND_TEST_CLASS(LinearProbingHashTableTestBase);
+ FRIEND_TEST_CLASS(LinearProbingHashTableTest);
public:
// an EntryType object is stored in the table
@@ -450,581 +411,7 @@ namespace ds{
};
- ///////////////////////// testing ////////////////////////
-
- typedef LinearProbingHashTable > _LinProbHT8;
- typedef LinearProbingHashTable > _LinProbHT16;
- typedef LinearProbingHashTable > _LinProbHT32;
-
- template<
- class IntHashTable8 = _LinProbHT8,
- class IntHashTable16 = _LinProbHT16,
- class IntHashTable32 = _LinProbHT32
- >
- class LinearProbingHashTableTestBase : public yasi::Test{
- protected:
- //typedef LinearProbingHashTable > IntHashTable8;
- //typedef LinearProbingHashTable > IntHashTable16;
- //typedef LinearProbingHashTable > IntHashTable32;
- typedef IntHashTable16 IntHashTable; // default
- typedef typename IntHashTable::BucketType BucketType;
- typedef typename IntHashTable::BucketEntryPtr BucketEntryPtr;
- typedef typename IntHashTable::Pair Pair;
-
-
- public:
-
- virtual void insert(){
-
- IntHashTable h(4);
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(0, h.population()) << "pop not 0";
-
- {
- SCOPED_TRACE("insert 4-5-6");
- h.insert(4, 4);
- h.insert(5, 5);
- h.insert(6, 6);
- //// - - - - 4 5 6 - - - - - - - - -
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << h;
- ASSERT_EQ(3, h.population()) << "pop not 3";
- }
- {
- string str = "insert 20, which should go to bucket 7";
- int key = 20;
- int bucket = 7;
- SCOPED_TRACE(str);
- BucketEntryPtr p = h.insertKey(key);
- p->value = key; // assign the value
- cout << str << endl << h;
- ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
- ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
- ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
- }
- {
- string str = "insert 23, which should go to bucket 8";
- int key = 23;
- int bucket = 8;
- SCOPED_TRACE(str);
- BucketEntryPtr p = h.insertKey(key);
- p->value = key; // assign the value
- cout << str << endl << h;
- ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
- ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
- ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
- }
- {
- string str = "insert 20, which is already in bucket 7";
- SCOPED_TRACE(str);
- int key = 20;
- int bucket = 7;
- SCOPED_TRACE(str);
- BucketEntryPtr p = h.insertKey(key);
- cout << str << endl << h;
- ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
- ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
- ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
- ASSERT_EQ(key, h.value(bucket)) << key << " not in bucket " << bucket;
- }
- {
- h.insert(13, 13);
- h.insert(14, 14);
- h.insert(15, 15);
- //
- string str = "search round the table; now insert 16, should go to bucket 0";
- int key = 16;
- int bucket = 0;
- SCOPED_TRACE(str);
- BucketEntryPtr p = h.insertKey(key);
- p->value = key; // assign the value
- cout << str << endl << h;
- ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
- ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
- ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
-
- }
- {
- string str = "search round the table; now insert 29, should go to bucket 1";
- int key = 29;
- int bucket = 1;
- SCOPED_TRACE(str);
- BucketEntryPtr p = h.insertKey(key);
- p->value = key; // assign the value
- cout << str << endl << h;
- ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
- ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
- ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
-
- }
- {
- string str = "insert 0; should go to the zero element";
- int key = 0;
- SCOPED_TRACE(str);
- ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
- int prevPop = h.population();
- BucketEntryPtr p = h.insertKey(key);
- p->value = 100; // assign the value
- cout << str << endl << h;
- ASSERT_EQ(true, h._zeroUsed) << "_zeroUsed false";
- ASSERT_EQ(p, &h._zeroKeyEntry) << " zero key not in zeroPair";
- ASSERT_EQ(prevPop + 1, h.population()) << "population did not increase";
- ASSERT_EQ(key, h._zeroKeyEntry.key) << key << " not in zeroPair";
- }
-
- }
-
- virtual void copyTable(){
-
- IntHashTable16 h1(4);
- IntHashTable32 h2(5);
-
- ASSERT_EQ(16, h1.size()) << "h1 size not 16";
- ASSERT_EQ(0, h1.population()) << "h1 pop not 0";
- ASSERT_EQ(32, h2.size()) << "h2 size not 32";
- ASSERT_EQ(0, h2.population()) << "h2 pop not 0";
-
- int h1MaxItemsWithoutGrow = (int)(h1.size() * h1.DENSITY_MAX) - 1;
- // add some items to h1 without triggering grow()
- int* pKeys = new int[h1MaxItemsWithoutGrow];
- srand((unsigned int)time(NULL));
- for (int i = 0; i < h1MaxItemsWithoutGrow; i++){
- pKeys[i] = rand();
- h1.insert(pKeys[i], pKeys[i]);
- }
- ASSERT_EQ(16, h1.size()) << "h1 size not 16";
- ASSERT_EQ(h1MaxItemsWithoutGrow, h1.population()) << "h1 pop not " << h1MaxItemsWithoutGrow;
-
- // now copy items from h1 to h2
- h2.copyTable(h1.table, h1.size());
-
- // check that all items exist
- // items are rehashed mod 32
- for (int i = 0; i < h1._size; i++){
- if (!h1.isNull(i)){
- int key = h1.key(i);
- int value = h1.value(i);
- BucketEntryPtr p = h2.lookupKey(key);
- ASSERT_NE(0, (int)p) << "key " << key << " not found in h2";
- ASSERT_EQ(key, p->key) << "key " << key << " mismatch in h2 key " << p->key;
- ASSERT_EQ(value, p->value) << "value " << value << " mismatch in h2 key " << p->key << "value: " << p->value;
- }
- }
-
- DELETE_ARR_SAFE(pKeys);
- }
-
- virtual void remove(){
-
- IntHashTable h(4);
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(0, h.population()) << "pop not 0";
-
- {
- SCOPED_TRACE("insert 4-5-6-7");
- h.insert(4, 4);
- h.insert(5, 5);
- h.insert(6, 6);
- h.insert(7, 7);
- // add 12, and three more that map into 14
- h.insert(12, 12);
- h.insert(14, 14);
- h.insert(30, 30);
- h.insert(46, 46);
- //// 46 - - - 4 5 6 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << h;
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(8, h.population()) << "population not 8";
-
- }
- {
- SCOPED_TRACE("remove 4");
- h.remove(4);
- //// 46 - - - - 5 6 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << h;
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(4)) << "key " << 4 << " not NULL";
- ASSERT_EQ(5, h.key(5)) << "[5] not " << 5;
- ASSERT_EQ(6, h.key(6)) << "[6] not " << 6;
- ASSERT_EQ(7, h.key(7)) << "[7] not " << 7;
- ASSERT_EQ(7, h.population()) << "population not 7";
- }
- {
- SCOPED_TRACE("remove 6");
- cout << "before removing 6\n" << h;
- h.remove(6);
- //// 46 - - - - 5 - 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(6, h.population()) << "population not 6";
- cout << "after removing 6\n" << h;
- ASSERT_EQ(NULL, (int)h.lookupKey(6)) << "key " << 6 << " not NULL";
- ASSERT_EQ(5, h.key(5)) << "key " << 5 << " misplaced";
- ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
- }
- {
- SCOPED_TRACE("remove simple 2-item bucket");
- h.insert(85, 85);
- //// 46 - - - - 5 85 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(7, h.population()) << "population not 7";
- ASSERT_EQ(85, h.key(6)) << "[6] not 85";
- ASSERT_EQ(16, h.size()) << "size not 16";
- h.remove(5);
- //// 46 - - - - 85 - 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(NULL, (int)h.lookupKey(5)) << "key " << 5 << " not NULL";
- ASSERT_EQ(85, h.key(5)) << "key " << 85 << " misplaced";
- ASSERT_EQ(true, h.isNull(6)) << "[6] not NULL";
- ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
-
- // put 5 back
- h.insert(5, 5);
- //// 46 - - - - 85 5 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(5, h.key(6)) << "key " << 5 << " misplaced";
-
- // delete 5
- h.remove(5);
- //// 46 - - - - 85 - 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(5)) << "key " << 5 << " not NULL";
- ASSERT_EQ(85, h.key(5)) << "key " << 85 << " misplaced";
- ASSERT_EQ(true, h.isNull(6)) << "[6) not NULL";
- ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
-
- // for consistency later on, replace 85 with 5
- h.remove(85);
- h.insert(5, 5);
- //// 46 - - - - 5 - 7 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(5, h.key(5)) << "[5] not 5";
- ASSERT_EQ(true, h.isNull(6)) << "[6] not NULL";
- ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
- }
-
- // add several entries that map into bucket 5
- h.insert(21, 21);
- h.insert(37, 37);
- h.insert(53, 53);
- //// 46 - - - - 5 21 7 37 53 - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-
- // add several entries that map into bucket 7
- h.insert(23, 23);
- h.insert(39, 39);
- //// 46 - - - - 5 21 7 37 53 23 39 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-
- {
- SCOPED_TRACE("remove key from multi-item bucket");
-
- {
- SCOPED_TRACE("remove 5");
- //// 46 - - - - 5 21 7 37 53 23 39 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- // delete 5
- cout << "before removing 5:\n" << h << endl;
- h.remove(5);
- //// 46 - - - - 21 37 7 53 23 39 - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << "after removing 5:\n" << h << endl;
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(5)) << "key " << 5 << " not NULL";
- ASSERT_EQ(21, h.key(5)) << "[5] not 21";
- ASSERT_EQ(37, h.key(6)) << "[6] not 37";
- ASSERT_EQ(7, h.key(7)) << "[7] not 7";
- ASSERT_EQ(53, h.key(8)) << "[8] not 53";
- ASSERT_EQ(23, h.key(9)) << "[9] not 23";
- ASSERT_EQ(39, h.key(10)) << "[10] not 39";
- ASSERT_EQ(true, h.isNull(11)) << "[11] not NULL";
- ASSERT_EQ(12, h.key(12)) << "[12] not 12";
- ASSERT_EQ(true, h.isNull(13)) << "[13] not NULL";
- ASSERT_EQ(14, h.key(14)) << "[14] not 14";
- }
- {
- SCOPED_TRACE("remove 21");
- //delete 21
- h.remove(21);
- //// 46 - - - - 37 53 7 23 39 - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(21)) << "key " << 21 << " not NULL";
- ASSERT_EQ(37, h.key(5)) << "[5] not 37";
- ASSERT_EQ(53, h.key(6)) << "[6] not 53";
- ASSERT_EQ(7, h.key(7)) << "[7] not 7";
- ASSERT_EQ(23, h.key(8)) << "[8] not 23";
- ASSERT_EQ(39, h.key(9)) << "[9] not 39";
- ASSERT_EQ(true, h.isNull(10)) << "[10] not NULL";
- ASSERT_EQ(true, h.isNull(11)) << "[11] not NULL";
- ASSERT_EQ(12, h.key(12)) << "[12] not 12";
- ASSERT_EQ(true, h.isNull(13)) << "[13] not NULL";
- ASSERT_EQ(14, h.key(14)) << "[14] not 14";
- }
-
- {
- SCOPED_TRACE("remove 23");
- //delete 23
- h.remove(23);
- //// 46 - - - - 37 53 7 39 - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(23)) << "key " << 23 << " not NULL";
- ASSERT_EQ(37, h.key(5)) << "[5] not 37";
- ASSERT_EQ(53, h.key(6)) << "[6] not 53";
- ASSERT_EQ(7, h.key(7)) << "[7] not 7";
- ASSERT_EQ(39, h.key(8)) << "[8] not 39";
- ASSERT_EQ(true, h.isNull(9)) << "[9] not NULL";
- ASSERT_EQ(true, h.isNull(10)) << "[10] not NULL";
- ASSERT_EQ(true, h.isNull(11)) << "[11] not NULL";
- ASSERT_EQ(12, h.key(12)) << "[12] not 12";
- ASSERT_EQ(true, h.isNull(13)) << "[13] not NULL";
- ASSERT_EQ(14, h.key(14)) << "[14] not 14";
- }
- h.insert(62, 62);
- h.insert(17, 17);
- //// 46 62 17 - - 37 53 39 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(62, h.key(1)) << "[1] not 62";
-
- {
- SCOPED_TRACE("remove 46");
- //delete 46
- cout << "before removing 46:\n" << h << endl;
- h.remove(46);
- //// 62 17 - - - 37 53 39 - - - - 12 - 14 30
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- cout << "after removing 46:\n" << h << endl;
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(46)) << "key " << 46 << " not NULL";
- ASSERT_EQ(62, h.key(0)) << "[0] not 62";
- ASSERT_EQ(17, h.key(1)) << "[1] not 17";
- ASSERT_EQ(true, h.isNull(2)) << "[2] not NULL";
- ASSERT_EQ(14, h.key(14)) << "[14] not 14";
- ASSERT_EQ(30, h.key(15)) << "[15] not 30";
- }
-
- {
- SCOPED_TRACE("remove 30");
- //delete 30
- h.remove(30);
- //// - 17 - - - 37 53 39 - - - - 12 - 14 62
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(30)) << "key " << 30 << " not NULL";
- ASSERT_EQ(17, h.key(1)) << "[1] not 17";
- ASSERT_EQ(true, h.isNull(0)) << "[0] not NULL";
- ASSERT_EQ(14, h.key(14)) << "[14] not 14";
- ASSERT_EQ(62, h.key(15)) << "[15] not 62";
- }
- {
- SCOPED_TRACE("remove 14");
- //delete 14
- h.remove(14);
- //// - - - - - 37 53 39 - - - - 12 - 62 -
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- ASSERT_EQ(16, h.size()) << "size not 16";
- ASSERT_EQ(NULL, (int)h.lookupKey(14)) << "key " << 14 << " not NULL";
- ASSERT_EQ(62, h.key(14)) << "[14] not 62";
- ASSERT_EQ(true, h.isNull(15)) << "[15] not NULL";
- }
-
- }
- {
- SCOPED_TRACE("Zero key");
- ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
- h.insert(0, 5);
- ASSERT_EQ(true, h._zeroUsed) << "_zeroUsed false";
- ASSERT_EQ(0, h._zeroKeyEntry.key) << "key of zero element non-zero";
- ASSERT_EQ(5, h._zeroKeyEntry.value) << "key of zero element non-zero";
-
- // now delete 0
- h.remove(0);
- ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
- ASSERT_EQ(0, h._zeroKeyEntry.key) << "key of zero element non-zero";
- }
- {
- SCOPED_TRACE("remove from an all-filled table");
- // create a pathological table
- BucketType* badTable = h.allocTable(8); // 8 entries in the table
- for (int i = 0; i < 8; i++){
- badTable[i] = Pair(5, i); // all keys are 5
- }
- IntHashTable8 h2(3); // 8 elements to begin
- h2.forceTable(badTable, 3, 8, h2.hash); // make the table full
- ASSERT_EQ(8, h2.size()) << "size not 8";
- ASSERT_EQ(8, h2.population()) << "population not 8";
- ASSERT_EQ(8, h2.hash.modulus) << "has mod not 8";
-
- {
- SCOPED_TRACE("delete from index 5");
- cout << "before removing 5:\n" << h2;
- h2.remove(5);
- cout << "after removing 5:\n" << h2;
-
- ASSERT_EQ(7, h2.population()) << "population not 7";
- for (int i = 0; i < 8; i++){
- if (i == 4){
- ASSERT_EQ(true, h2.isNull(i)) << "[" << i << "] not NULL";
- }
- else
- ASSERT_EQ(5, h2.key(i)) << "[" << i << "] not 5";
- }
- }
-
- // cleanup
- }
-
- }
-
- virtual void growCondition(){
- IntHashTable16 h1(4);
-
- int i = 0;
- {
- SCOPED_TRACE("grow");
- ASSERT_EQ(16, h1.size()) << "h1 size not 16";
- ASSERT_EQ(0, h1.population()) << "h1 pop not " << 0;
- int popLimit = h1.maxPopulationWithoutGrow();
- for (; i < popLimit; i++){
- h1.insert(i, i);
- }
- ASSERT_EQ(16, h1.size()) << "h1 size not 16";
- ASSERT_EQ(popLimit, h1.population()) << "h1 pop not " << popLimit;
- cout << h1;
- // this should trigger grow
- h1.insert(i, i);
- i++;
- ASSERT_EQ(32, h1.size()) << "h1 size not 32";
- ASSERT_EQ(popLimit + 1, h1.population()) << "h1 pop not " << popLimit + 1;
- }
- }
- virtual void shrinkCondition(){
- IntHashTable16 h1(4);
-
- int i = 0;
- {
- SCOPED_TRACE("shrink");
- // put as many items as we can without invoking grow()
- int popLimit = h1.maxPopulationWithoutGrow();
- for (; i < popLimit; i++){
- h1.insert(i, i);
- }
- ASSERT_EQ(16, h1.size()) << "h1 size not 16";
- ASSERT_EQ(popLimit, h1.population()) << "h1 pop not " << popLimit;
-
- popLimit = h1.minPopulationWithoutShrink();
- int removeCount = h1._population - popLimit;
- int j = 0;
- for (; j < removeCount; j++){
- h1.remove(j);
- }
- ASSERT_EQ(16, h1.size()) << "h1 size not 16";
- ASSERT_EQ(popLimit, h1.population()) << "h1 pop not " << popLimit;
- // this should trigger shrink
- h1.remove(j);
- j--;
- ASSERT_EQ(8, h1.size()) << "h1 size not " << 8;
- ASSERT_EQ(popLimit - 1, h1.population()) << "h1 pop not " << popLimit - 1;
- }
- }
-
- virtual void isKeyZero(){
- //struct MyStruct{
- // int a;
- // float b;
- // char c;
- // bool operator==(const MyStruct& other) const { return a == other.a && b == other.b && c == other.c; }
- // bool operator!=(const MyStruct& other) const { return !(*this == other); }
- // bool operator<(const MyStruct& other) const { return a < other.a && b < other.b && c < other.c; }
- // bool operator<=(const MyStruct& other) const { return a <= other.a && b <= other.b && c <= other.c; }
- //};
- //struct MyHashFunction{
- //public:
- // virtual int operator()(const MyStruct& n)const{ return n.a + (int)n.b + (int)n.c; }
- //};
- //
- //// create a hashtable with MyStruct as Key
- //LinearProbingHashTable,MyHashFunction> h(1);
-
- IntHashTable h;
- IntHashTable* pH = new IntHashTable();
- delete pH;
-
- //h.insert(4, 4);
- //h.insert(5, 5);
- //h.insert(6, 6);
-
- //// now test
- //ASSERT_EQ(false, h.isNull(4)) << "4 zero";
- //ASSERT_EQ(false, h.isNull(5)) << "5 zero";
- //ASSERT_EQ(false, h.isNull(6)) << "6 zero";
- //ASSERT_EQ(true, h.isNull(3)) << "3 not zero";
- //ASSERT_EQ(true, h.isNull(0)) << "0 not zero";
- //
- //h.setKeyZero(h.keyptr(4));
- //ASSERT_EQ(true, h.isNull(4)) << "4 not null";
- //h.makeNull(5);
- //ASSERT_EQ(true, h.isNull(5)) << "5 not null";
- //*h.keyptr(6) = 0;
- //ASSERT_EQ(true, h.isNull(6)) << "6 not null";
- //h.entryptr(2)->key = 2;
- //ASSERT_EQ(2, h.key(2)) << "[2] not 2";
- //h.removeEntry(2);
- //ASSERT_EQ(true, h.isNull(2)) << "[2] not NULL";
- //h.makeEntry(4, 44);
- //ASSERT_EQ(44, h.key(4)) << "[4] not 44";
-
-
- {
- class A{
- public:
- virtual void x() = 0;
- };
-
- class BB :public A{
- public:
- int key;
- int value;
- void x() override {}
- };
-
- //typedef BB B;
- //typedef KVPairSimple B;
- typedef KVPair B;
- int n = 1;
- int s = sizeof(B);
- B* pArr = (B*)malloc(n * s);
- //B* pArr = new B[n];
- memset(pArr, 0, s * n);
- //std::fill((char*)pArr, (char*)(pArr+n), 0);
- //for (int i = 0; i < n; i++){
- // pArr[i].key = 0xAAAAAAAA;
- // pArr[i].value = 0xBBBBBBBB;
- //}
- //delete[] pArr;
- free(pArr);
- }
-
- //ASSERT_EQ(0, 1) << "uncomment other tests";
- }
- };
-
- class LinearProbingHashTableTest : public LinearProbingHashTableTestBase < _LinProbHT8, _LinProbHT16, _LinProbHT32> {
- public:
- // inherit all tests from base
- };
- ADD_TEST_F(LinearProbingHashTableTest, insert);
- ADD_TEST_F(LinearProbingHashTableTest, copyTable);
- ADD_TEST_F(LinearProbingHashTableTest, growCondition);
- ADD_TEST_F(LinearProbingHashTableTest, shrinkCondition);
- ADD_TEST_F(LinearProbingHashTableTest, isKeyZero);
- ADD_TEST_F(LinearProbingHashTableTest, remove);
diff --git a/YASI_12/ds.LinearProbingHashTable.test.h b/YASI_12/ds.LinearProbingHashTable.test.h
new file mode 100644
index 0000000..6536ce6
--- /dev/null
+++ b/YASI_12/ds.LinearProbingHashTable.test.h
@@ -0,0 +1,586 @@
+#pragma once
+
+#if YASI_TEST_THIS_MODULE != 1
+#define YASI_TEST_THIS_MODULE 1
+#endif
+#include "ds.LinearProbingHashTable.h"
+
+namespace yasi{
+ namespace ds{
+ ///////////////////////// testing ////////////////////////
+
+ typedef LinearProbingHashTable > _LinProbHT8;
+ typedef LinearProbingHashTable > _LinProbHT16;
+ typedef LinearProbingHashTable > _LinProbHT32;
+
+ template<
+ class IntHashTable8 = _LinProbHT8,
+ class IntHashTable16 = _LinProbHT16,
+ class IntHashTable32 = _LinProbHT32
+ >
+ class LinearProbingHashTableTestBase : public yasi::Test{
+ protected:
+ //typedef LinearProbingHashTable > IntHashTable8;
+ //typedef LinearProbingHashTable > IntHashTable16;
+ //typedef LinearProbingHashTable > IntHashTable32;
+ typedef IntHashTable16 IntHashTable; // default
+ typedef typename IntHashTable::BucketType BucketType;
+ typedef typename IntHashTable::BucketEntryPtr BucketEntryPtr;
+ typedef typename IntHashTable::Pair Pair;
+
+
+ public:
+
+ virtual void insert(){
+
+ IntHashTable h(4);
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(0, h.population()) << "pop not 0";
+
+ {
+ SCOPED_TRACE("insert 4-5-6");
+ h.insert(4, 4);
+ h.insert(5, 5);
+ h.insert(6, 6);
+ //// - - - - 4 5 6 - - - - - - - - -
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << h;
+ ASSERT_EQ(3, h.population()) << "pop not 3";
+ }
+ {
+ string str = "insert 20, which should go to bucket 7";
+ int key = 20;
+ int bucket = 7;
+ SCOPED_TRACE(str);
+ BucketEntryPtr p = h.insertKey(key);
+ p->value = key; // assign the value
+ cout << str << endl << h;
+ ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
+ ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
+ ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
+ }
+ {
+ string str = "insert 23, which should go to bucket 8";
+ int key = 23;
+ int bucket = 8;
+ SCOPED_TRACE(str);
+ BucketEntryPtr p = h.insertKey(key);
+ p->value = key; // assign the value
+ cout << str << endl << h;
+ ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
+ ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
+ ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
+ }
+ {
+ string str = "insert 20, which is already in bucket 7";
+ SCOPED_TRACE(str);
+ int key = 20;
+ int bucket = 7;
+ SCOPED_TRACE(str);
+ BucketEntryPtr p = h.insertKey(key);
+ cout << str << endl << h;
+ ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
+ ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
+ ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
+ ASSERT_EQ(key, h.value(bucket)) << key << " not in bucket " << bucket;
+ }
+ {
+ h.insert(13, 13);
+ h.insert(14, 14);
+ h.insert(15, 15);
+ //
+ string str = "search round the table; now insert 16, should go to bucket 0";
+ int key = 16;
+ int bucket = 0;
+ SCOPED_TRACE(str);
+ BucketEntryPtr p = h.insertKey(key);
+ p->value = key; // assign the value
+ cout << str << endl << h;
+ ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
+ ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
+ ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
+
+ }
+ {
+ string str = "search round the table; now insert 29, should go to bucket 1";
+ int key = 29;
+ int bucket = 1;
+ SCOPED_TRACE(str);
+ BucketEntryPtr p = h.insertKey(key);
+ p->value = key; // assign the value
+ cout << str << endl << h;
+ ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
+ ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
+ ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
+
+ }
+ {
+ string str = "insert 0; should go to the zero element";
+ int key = 0;
+ SCOPED_TRACE(str);
+ ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
+ int prevPop = h.population();
+ BucketEntryPtr p = h.insertKey(key);
+ p->value = 100; // assign the value
+ cout << str << endl << h;
+ ASSERT_EQ(true, h._zeroUsed) << "_zeroUsed false";
+ ASSERT_EQ(p, &h._zeroKeyEntry) << " zero key not in zeroPair";
+ ASSERT_EQ(prevPop + 1, h.population()) << "population did not increase";
+ ASSERT_EQ(key, h._zeroKeyEntry.key) << key << " not in zeroPair";
+ }
+
+ }
+
+ virtual void copyTable(){
+
+ IntHashTable16 h1(4);
+ IntHashTable32 h2(5);
+
+ ASSERT_EQ(16, h1.size()) << "h1 size not 16";
+ ASSERT_EQ(0, h1.population()) << "h1 pop not 0";
+ ASSERT_EQ(32, h2.size()) << "h2 size not 32";
+ ASSERT_EQ(0, h2.population()) << "h2 pop not 0";
+
+ int h1MaxItemsWithoutGrow = (int)(h1.size() * h1.DENSITY_MAX) - 1;
+ // add some items to h1 without triggering grow()
+ int* pKeys = new int[h1MaxItemsWithoutGrow];
+ srand((unsigned int)time(NULL));
+ for (int i = 0; i < h1MaxItemsWithoutGrow; i++){
+ pKeys[i] = rand();
+ h1.insert(pKeys[i], pKeys[i]);
+ }
+ ASSERT_EQ(16, h1.size()) << "h1 size not 16";
+ ASSERT_EQ(h1MaxItemsWithoutGrow, h1.population()) << "h1 pop not " << h1MaxItemsWithoutGrow;
+
+ // now copy items from h1 to h2
+ h2.copyTable(h1.table, h1.size());
+
+ // check that all items exist
+ // items are rehashed mod 32
+ for (int i = 0; i < h1._size; i++){
+ if (!h1.isNull(i)){
+ int key = h1.key(i);
+ int value = h1.value(i);
+ BucketEntryPtr p = h2.lookupKey(key);
+ ASSERT_NE(0, (int)p) << "key " << key << " not found in h2";
+ ASSERT_EQ(key, p->key) << "key " << key << " mismatch in h2 key " << p->key;
+ ASSERT_EQ(value, p->value) << "value " << value << " mismatch in h2 key " << p->key << "value: " << p->value;
+ }
+ }
+
+ DELETE_ARR_SAFE(pKeys);
+ }
+
+ virtual void remove(){
+
+ IntHashTable h(4);
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(0, h.population()) << "pop not 0";
+
+ {
+ SCOPED_TRACE("insert 4-5-6-7");
+ h.insert(4, 4);
+ h.insert(5, 5);
+ h.insert(6, 6);
+ h.insert(7, 7);
+ // add 12, and three more that map into 14
+ h.insert(12, 12);
+ h.insert(14, 14);
+ h.insert(30, 30);
+ h.insert(46, 46);
+ //// 46 - - - 4 5 6 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << h;
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(8, h.population()) << "population not 8";
+
+ }
+ {
+ SCOPED_TRACE("remove 4");
+ h.remove(4);
+ //// 46 - - - - 5 6 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << h;
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(4)) << "key " << 4 << " not NULL";
+ ASSERT_EQ(5, h.key(5)) << "[5] not " << 5;
+ ASSERT_EQ(6, h.key(6)) << "[6] not " << 6;
+ ASSERT_EQ(7, h.key(7)) << "[7] not " << 7;
+ ASSERT_EQ(7, h.population()) << "population not 7";
+ }
+ {
+ SCOPED_TRACE("remove 6");
+ cout << "before removing 6\n" << h;
+ h.remove(6);
+ //// 46 - - - - 5 - 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(6, h.population()) << "population not 6";
+ cout << "after removing 6\n" << h;
+ ASSERT_EQ(NULL, (int)h.lookupKey(6)) << "key " << 6 << " not NULL";
+ ASSERT_EQ(5, h.key(5)) << "key " << 5 << " misplaced";
+ ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
+ }
+ {
+ SCOPED_TRACE("remove simple 2-item bucket");
+ h.insert(85, 85);
+ //// 46 - - - - 5 85 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(7, h.population()) << "population not 7";
+ ASSERT_EQ(85, h.key(6)) << "[6] not 85";
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ h.remove(5);
+ //// 46 - - - - 85 - 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(NULL, (int)h.lookupKey(5)) << "key " << 5 << " not NULL";
+ ASSERT_EQ(85, h.key(5)) << "key " << 85 << " misplaced";
+ ASSERT_EQ(true, h.isNull(6)) << "[6] not NULL";
+ ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
+
+ // put 5 back
+ h.insert(5, 5);
+ //// 46 - - - - 85 5 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(5, h.key(6)) << "key " << 5 << " misplaced";
+
+ // delete 5
+ h.remove(5);
+ //// 46 - - - - 85 - 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(5)) << "key " << 5 << " not NULL";
+ ASSERT_EQ(85, h.key(5)) << "key " << 85 << " misplaced";
+ ASSERT_EQ(true, h.isNull(6)) << "[6) not NULL";
+ ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
+
+ // for consistency later on, replace 85 with 5
+ h.remove(85);
+ h.insert(5, 5);
+ //// 46 - - - - 5 - 7 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(5, h.key(5)) << "[5] not 5";
+ ASSERT_EQ(true, h.isNull(6)) << "[6] not NULL";
+ ASSERT_EQ(7, h.key(7)) << "key " << 7 << " misplaced";
+ }
+
+ // add several entries that map into bucket 5
+ h.insert(21, 21);
+ h.insert(37, 37);
+ h.insert(53, 53);
+ //// 46 - - - - 5 21 7 37 53 - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+
+ // add several entries that map into bucket 7
+ h.insert(23, 23);
+ h.insert(39, 39);
+ //// 46 - - - - 5 21 7 37 53 23 39 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+
+ {
+ SCOPED_TRACE("remove key from multi-item bucket");
+
+ {
+ SCOPED_TRACE("remove 5");
+ //// 46 - - - - 5 21 7 37 53 23 39 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ // delete 5
+ cout << "before removing 5:\n" << h << endl;
+ h.remove(5);
+ //// 46 - - - - 21 37 7 53 23 39 - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << "after removing 5:\n" << h << endl;
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(5)) << "key " << 5 << " not NULL";
+ ASSERT_EQ(21, h.key(5)) << "[5] not 21";
+ ASSERT_EQ(37, h.key(6)) << "[6] not 37";
+ ASSERT_EQ(7, h.key(7)) << "[7] not 7";
+ ASSERT_EQ(53, h.key(8)) << "[8] not 53";
+ ASSERT_EQ(23, h.key(9)) << "[9] not 23";
+ ASSERT_EQ(39, h.key(10)) << "[10] not 39";
+ ASSERT_EQ(true, h.isNull(11)) << "[11] not NULL";
+ ASSERT_EQ(12, h.key(12)) << "[12] not 12";
+ ASSERT_EQ(true, h.isNull(13)) << "[13] not NULL";
+ ASSERT_EQ(14, h.key(14)) << "[14] not 14";
+ }
+ {
+ SCOPED_TRACE("remove 21");
+ //delete 21
+ h.remove(21);
+ //// 46 - - - - 37 53 7 23 39 - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(21)) << "key " << 21 << " not NULL";
+ ASSERT_EQ(37, h.key(5)) << "[5] not 37";
+ ASSERT_EQ(53, h.key(6)) << "[6] not 53";
+ ASSERT_EQ(7, h.key(7)) << "[7] not 7";
+ ASSERT_EQ(23, h.key(8)) << "[8] not 23";
+ ASSERT_EQ(39, h.key(9)) << "[9] not 39";
+ ASSERT_EQ(true, h.isNull(10)) << "[10] not NULL";
+ ASSERT_EQ(true, h.isNull(11)) << "[11] not NULL";
+ ASSERT_EQ(12, h.key(12)) << "[12] not 12";
+ ASSERT_EQ(true, h.isNull(13)) << "[13] not NULL";
+ ASSERT_EQ(14, h.key(14)) << "[14] not 14";
+ }
+
+ {
+ SCOPED_TRACE("remove 23");
+ //delete 23
+ h.remove(23);
+ //// 46 - - - - 37 53 7 39 - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(23)) << "key " << 23 << " not NULL";
+ ASSERT_EQ(37, h.key(5)) << "[5] not 37";
+ ASSERT_EQ(53, h.key(6)) << "[6] not 53";
+ ASSERT_EQ(7, h.key(7)) << "[7] not 7";
+ ASSERT_EQ(39, h.key(8)) << "[8] not 39";
+ ASSERT_EQ(true, h.isNull(9)) << "[9] not NULL";
+ ASSERT_EQ(true, h.isNull(10)) << "[10] not NULL";
+ ASSERT_EQ(true, h.isNull(11)) << "[11] not NULL";
+ ASSERT_EQ(12, h.key(12)) << "[12] not 12";
+ ASSERT_EQ(true, h.isNull(13)) << "[13] not NULL";
+ ASSERT_EQ(14, h.key(14)) << "[14] not 14";
+ }
+ h.insert(62, 62);
+ h.insert(17, 17);
+ //// 46 62 17 - - 37 53 39 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(62, h.key(1)) << "[1] not 62";
+
+ {
+ SCOPED_TRACE("remove 46");
+ //delete 46
+ cout << "before removing 46:\n" << h << endl;
+ h.remove(46);
+ //// 62 17 - - - 37 53 39 - - - - 12 - 14 30
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ cout << "after removing 46:\n" << h << endl;
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(46)) << "key " << 46 << " not NULL";
+ ASSERT_EQ(62, h.key(0)) << "[0] not 62";
+ ASSERT_EQ(17, h.key(1)) << "[1] not 17";
+ ASSERT_EQ(true, h.isNull(2)) << "[2] not NULL";
+ ASSERT_EQ(14, h.key(14)) << "[14] not 14";
+ ASSERT_EQ(30, h.key(15)) << "[15] not 30";
+ }
+
+ {
+ SCOPED_TRACE("remove 30");
+ //delete 30
+ h.remove(30);
+ //// - 17 - - - 37 53 39 - - - - 12 - 14 62
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(30)) << "key " << 30 << " not NULL";
+ ASSERT_EQ(17, h.key(1)) << "[1] not 17";
+ ASSERT_EQ(true, h.isNull(0)) << "[0] not NULL";
+ ASSERT_EQ(14, h.key(14)) << "[14] not 14";
+ ASSERT_EQ(62, h.key(15)) << "[15] not 62";
+ }
+ {
+ SCOPED_TRACE("remove 14");
+ //delete 14
+ h.remove(14);
+ //// - - - - - 37 53 39 - - - - 12 - 62 -
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ASSERT_EQ(16, h.size()) << "size not 16";
+ ASSERT_EQ(NULL, (int)h.lookupKey(14)) << "key " << 14 << " not NULL";
+ ASSERT_EQ(62, h.key(14)) << "[14] not 62";
+ ASSERT_EQ(true, h.isNull(15)) << "[15] not NULL";
+ }
+
+ }
+ {
+ SCOPED_TRACE("Zero key");
+ ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
+ h.insert(0, 5);
+ ASSERT_EQ(true, h._zeroUsed) << "_zeroUsed false";
+ ASSERT_EQ(0, h._zeroKeyEntry.key) << "key of zero element non-zero";
+ ASSERT_EQ(5, h._zeroKeyEntry.value) << "key of zero element non-zero";
+
+ // now delete 0
+ h.remove(0);
+ ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
+ ASSERT_EQ(0, h._zeroKeyEntry.key) << "key of zero element non-zero";
+ }
+ {
+ SCOPED_TRACE("remove from an all-filled table");
+ // create a pathological table
+ BucketType* badTable = h.allocTable(8); // 8 entries in the table
+ for (int i = 0; i < 8; i++){
+ badTable[i] = Pair(5, i); // all keys are 5
+ }
+ IntHashTable8 h2(3); // 8 elements to begin
+ h2.forceTable(badTable, 3, 8, h2.hash); // make the table full
+ ASSERT_EQ(8, h2.size()) << "size not 8";
+ ASSERT_EQ(8, h2.population()) << "population not 8";
+ ASSERT_EQ(8, h2.hash.modulus) << "has mod not 8";
+
+ {
+ SCOPED_TRACE("delete from index 5");
+ cout << "before removing 5:\n" << h2;
+ h2.remove(5);
+ cout << "after removing 5:\n" << h2;
+
+ ASSERT_EQ(7, h2.population()) << "population not 7";
+ for (int i = 0; i < 8; i++){
+ if (i == 4){
+ ASSERT_EQ(true, h2.isNull(i)) << "[" << i << "] not NULL";
+ }
+ else
+ ASSERT_EQ(5, h2.key(i)) << "[" << i << "] not 5";
+ }
+ }
+
+ // cleanup
+ }
+
+ }
+
+ virtual void growCondition(){
+ IntHashTable16 h1(4);
+
+ int i = 0;
+ {
+ SCOPED_TRACE("grow");
+ ASSERT_EQ(16, h1.size()) << "h1 size not 16";
+ ASSERT_EQ(0, h1.population()) << "h1 pop not " << 0;
+ int popLimit = h1.maxPopulationWithoutGrow();
+ for (; i < popLimit; i++){
+ h1.insert(i, i);
+ }
+ ASSERT_EQ(16, h1.size()) << "h1 size not 16";
+ ASSERT_EQ(popLimit, h1.population()) << "h1 pop not " << popLimit;
+ cout << h1;
+ // this should trigger grow
+ h1.insert(i, i);
+ i++;
+ ASSERT_EQ(32, h1.size()) << "h1 size not 32";
+ ASSERT_EQ(popLimit + 1, h1.population()) << "h1 pop not " << popLimit + 1;
+ }
+ }
+ virtual void shrinkCondition(){
+ IntHashTable16 h1(4);
+
+ int i = 0;
+ {
+ SCOPED_TRACE("shrink");
+ // put as many items as we can without invoking grow()
+ int popLimit = h1.maxPopulationWithoutGrow();
+ for (; i < popLimit; i++){
+ h1.insert(i, i);
+ }
+ ASSERT_EQ(16, h1.size()) << "h1 size not 16";
+ ASSERT_EQ(popLimit, h1.population()) << "h1 pop not " << popLimit;
+
+ popLimit = h1.minPopulationWithoutShrink();
+ int removeCount = h1._population - popLimit;
+ int j = 0;
+ for (; j < removeCount; j++){
+ h1.remove(j);
+ }
+ ASSERT_EQ(16, h1.size()) << "h1 size not 16";
+ ASSERT_EQ(popLimit, h1.population()) << "h1 pop not " << popLimit;
+ // this should trigger shrink
+ h1.remove(j);
+ j--;
+ ASSERT_EQ(8, h1.size()) << "h1 size not " << 8;
+ ASSERT_EQ(popLimit - 1, h1.population()) << "h1 pop not " << popLimit - 1;
+ }
+ }
+
+ virtual void isKeyZero(){
+ //struct MyStruct{
+ // int a;
+ // float b;
+ // char c;
+ // bool operator==(const MyStruct& other) const { return a == other.a && b == other.b && c == other.c; }
+ // bool operator!=(const MyStruct& other) const { return !(*this == other); }
+ // bool operator<(const MyStruct& other) const { return a < other.a && b < other.b && c < other.c; }
+ // bool operator<=(const MyStruct& other) const { return a <= other.a && b <= other.b && c <= other.c; }
+ //};
+ //struct MyHashFunction{
+ //public:
+ // virtual int operator()(const MyStruct& n)const{ return n.a + (int)n.b + (int)n.c; }
+ //};
+ //
+ //// create a hashtable with MyStruct as Key
+ //LinearProbingHashTable,MyHashFunction> h(1);
+
+ IntHashTable h;
+ IntHashTable* pH = new IntHashTable();
+ delete pH;
+
+ //h.insert(4, 4);
+ //h.insert(5, 5);
+ //h.insert(6, 6);
+
+ //// now test
+ //ASSERT_EQ(false, h.isNull(4)) << "4 zero";
+ //ASSERT_EQ(false, h.isNull(5)) << "5 zero";
+ //ASSERT_EQ(false, h.isNull(6)) << "6 zero";
+ //ASSERT_EQ(true, h.isNull(3)) << "3 not zero";
+ //ASSERT_EQ(true, h.isNull(0)) << "0 not zero";
+ //
+ //h.setKeyZero(h.keyptr(4));
+ //ASSERT_EQ(true, h.isNull(4)) << "4 not null";
+ //h.makeNull(5);
+ //ASSERT_EQ(true, h.isNull(5)) << "5 not null";
+ //*h.keyptr(6) = 0;
+ //ASSERT_EQ(true, h.isNull(6)) << "6 not null";
+ //h.entryptr(2)->key = 2;
+ //ASSERT_EQ(2, h.key(2)) << "[2] not 2";
+ //h.removeEntry(2);
+ //ASSERT_EQ(true, h.isNull(2)) << "[2] not NULL";
+ //h.makeEntry(4, 44);
+ //ASSERT_EQ(44, h.key(4)) << "[4] not 44";
+
+
+ {
+ class A{
+ public:
+ virtual void x() = 0;
+ };
+
+ class BB :public A{
+ public:
+ int key;
+ int value;
+ void x() override {}
+ };
+
+ //typedef BB B;
+ //typedef KVPairSimple B;
+ typedef KVPair B;
+ int n = 1;
+ int s = sizeof(B);
+ B* pArr = (B*)malloc(n * s);
+ //B* pArr = new B[n];
+ memset(pArr, 0, s * n);
+ //std::fill((char*)pArr, (char*)(pArr+n), 0);
+ //for (int i = 0; i < n; i++){
+ // pArr[i].key = 0xAAAAAAAA;
+ // pArr[i].value = 0xBBBBBBBB;
+ //}
+ //delete[] pArr;
+ free(pArr);
+ }
+
+ //ASSERT_EQ(0, 1) << "uncomment other tests";
+ }
+ };
+
+ class LinearProbingHashTableTest : public LinearProbingHashTableTestBase < _LinProbHT8, _LinProbHT16, _LinProbHT32> {
+ public:
+ // inherit all tests from base
+ };
+
+ ADD_TEST_F(LinearProbingHashTableTest, insert);
+ ADD_TEST_F(LinearProbingHashTableTest, copyTable);
+ ADD_TEST_F(LinearProbingHashTableTest, growCondition);
+ ADD_TEST_F(LinearProbingHashTableTest, shrinkCondition);
+ ADD_TEST_F(LinearProbingHashTableTest, isKeyZero);
+ ADD_TEST_F(LinearProbingHashTableTest, remove);
+ }
+}
\ No newline at end of file
diff --git a/YASI_12/ds.SeparateChainingHashTable.h b/YASI_12/ds.SeparateChainingHashTable.h
index d573f63..d425ce1 100644
--- a/YASI_12/ds.SeparateChainingHashTable.h
+++ b/YASI_12/ds.SeparateChainingHashTable.h
@@ -1,7 +1,9 @@
#pragma once
-#include "common.h"
#include "ds.hashtablebase.h"
+// enable-disable testing classes in this file
+#include "test.this.module.h"
+
using namespace std;
namespace yasi{
@@ -22,7 +24,7 @@ namespace ds{
HashFunction>
{
///////////////// enable testing ///////////////////
- friend class SeparateChainingHashTableTest;
+ FRIEND_TEST_CLASS( SeparateChainingHashTableTest);
protected:
typedef KVPair Pair;
@@ -174,183 +176,6 @@ namespace ds{
};
- class SeparateChainingHashTableTest : public yasi::Test{
- protected:
- typedef SeparateChainingHashTable IntHashTable;
- typedef KVPair Pair;
- typedef IntHashTable::BucketType BucketType;
- typedef IntHashTable::ListType ListType;
-
- IntHashTable h;
- public:
-
- void hashCode(){
- // hashcode must be within range
- srand((unsigned int)time(NULL));
- for (int i = 0; i < 1000; i++){
- int n = rand();
- int index = h.index(n);
- ASSERT_LT(index, h.size()) << "index(" << n << ") out of range";
- ASSERT_GE(index, 0) << "index(" << n << ") out of range";
- }
- }
-
- void all(){
- srand((unsigned int)time(NULL));
- int logSize = h._logSize;
- int size = h._size;
- ASSERT_EQ(IntHashTable::INIT_LOGSIZE, logSize) << "initial logSize not " << IntHashTable::INIT_LOGSIZE;
- ASSERT_EQ(1 << logSize, h.size()) << "table size not 2^" << logSize;
- for (int i = 0; i < h.size(); i++){
- ASSERT_EQ(0, (int)h.table[i]) << "table[i] not NULL for i=" << i;
- }
-
- {
- SCOPED_TRACE("add items in hashtable");
- // now add 10 items; should not trigger grow()
- ASSERT_LT(10 / h.size(), h.DENSITY_MAX) << "10/" << h.size() << " items triggered grow() while DENSITY_MAX=" << h.DENSITY_MAX;
- for (int i = 0; i < 10; i++){
- h.put(i, i);
- }
- // test contains()
- for (int i = 0; i < 10; i++){
- ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found";
- }
- ASSERT_EQ(false, h.contains(100)) << "absent key 100 was found";
- // test get()
- for (int i = 0; i < 10; i++){
- int* pValue = h.get(i);
- ASSERT_NE(NULL, (int)pValue) << "pValue with key " << i << " NULL";
- ASSERT_EQ(i, *pValue) << "value with key " << i << " mismatch";
- }
- ASSERT_EQ(NULL, h.get(100)) << "absent key 100 was found";
- // test duplicate insert
- // should result in update
- h.put(5, -6);
- int* pValue = h.get(5);
- ASSERT_NE(NULL, (int)pValue) << "pValue with key " << 5 << " NULL";
- ASSERT_EQ(-6, *pValue) << "value with key " << 5 << " not -6";
-
- // now add some more but don't trigger grow()
- size = h.size();
- int maxCurrent = ((int)(h.size() * h.DENSITY_MAX)) - 1;
- int currentPop = h.population();
- for (int i = currentPop; i < maxCurrent; i++){
- // this insertion should not trigger grow()
- h.put(i, i);
- }
- ASSERT_EQ(maxCurrent, h.population()) << "population not maxCurrent";
- ASSERT_EQ(size, h.size()) << "size() not size";
- // this insertion should trigger grow()
- int key = rand();
- while (h.contains(key)){ key = rand(); }
- h.put(key, key); // should trigger grow()
- ASSERT_EQ(size * 2, h.size()) << "size() not 2*oldSize";
- ASSERT_EQ(maxCurrent + 1, h.population()) << "population() not maxCurrent+1";
- ASSERT_GE(0.375, h.density()) << "density() > 0.375";
-
- // print the table
- string str = h.toString();
- cout << "after grow(): " << endl << str << endl;
-
- // check that all old entries are still in the table
- for (int i = 0; i < 10; i++){ // first 10 entries
- int v = i;
- if (i == 5){
- // remember that we had updated an entry
- v = -6;
- }
- ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found in new table";
- ASSERT_EQ(v, *h.get(i)) << "value with key " << i << " incorrect in new table";
- }
- for (int i = currentPop; i < maxCurrent; i++){ // further entries till max capacity before grow
- ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found in new table";
- ASSERT_EQ(i, *h.get(i)) << "value with key " << i << " incorrect in new table";
- }
- // the entry that triggered grow()
- ASSERT_EQ(true, h.contains(key)) << "key " << key << " not found in new table";
- ASSERT_EQ(key, *h.get(key)) << " value with key " << key << " incorrect in new table";
- }
-
- {
- SCOPED_TRACE("remove");
- // now remove entries but do not trigger shrink()
- int i = 0;
- BucketType pCriticalBucket = NULL;
- DoublyLinkedList survivedKeys;
- int initPop = h.population();
- int numRemoved = 0;
- bool removedEnough = false;
- for (; i < h.size(); i++){
- BucketType pList = h.table[i];
- if (pList){
- // number of entries touched: either removed or copied
- int remainingItems = pList->size();
-
- while (remainingItems > 0){
- // check if we can remove this node without causing shrink()
- if (!removedEnough &&
- ((float)(h._population - 1)) / h._size <= h.DENSITY_MIN){
- // this deletion will cause shrink()
- pCriticalBucket = pList;
- removedEnough = true;
- }
- else{
- Pair kvPair = (*pList->begin());
- int key = kvPair.key;
- if (removedEnough == false){
- // remove an entry
- int prevPop = h._population;
- int prevSize = h.size();
- h.remove(key);
- ASSERT_EQ(prevPop - 1, h._population) << "remove did not work";
- ASSERT_EQ(h._size, prevSize) << "size changed by remove";
- ASSERT_EQ(false, h.contains(key)) << "removed key " << key << " still remains";
- numRemoved++;
- remainingItems--;
- }
- else{
- // copy this should-be-surviving entry into a list for further verification
- survivedKeys.push(kvPair);
- remainingItems--;
- }
- }
- }
- }
- } // for loop through all slots
-
- ASSERT_EQ(initPop - numRemoved, survivedKeys.size()) << "initSize+numRemoved not survivedKeys.size";
- if (removedEnough) {
- // ok; removing next key should cause shrink()
- int prevPop = h._population;
- int prevSize = h.size();
- int removedKey = (*pCriticalBucket->begin()).key;
- h.remove(removedKey);
- cout << "shrinked: \n" << h.toString() << endl;
- ASSERT_EQ(h._population, prevPop - 1) << "remove did not work";
- ASSERT_EQ(h._size, prevSize >> 1) << "size did not shrink by half";
- ASSERT_EQ(false, h.contains(removedKey)) << "removed key " << removedKey << " still remains";
-
- // now check that all should-have-survived keys are still present in the new table
- for (DoublyLinkedList::iterator it = survivedKeys.begin(); it != survivedKeys.end(); it++){
- int key = (*it).key;
- if (key == removedKey) continue; // this is the removed key that made the table shrink
- int value = (*it).value;
- ASSERT_EQ(true, h.contains(key)) << "key " << key << " absent in shrinked table";
- ASSERT_NE(NULL, (int)h.get(key)) << "get(" << key << ") is NULL in shrinked table";
- ASSERT_EQ(value, *(h.get(key))) << "get(" << key << ") not " << value << "in shrinked table";
- }
-
- }
- }
-
-
- //ASSERT_EQ(0, 1) << "incomplete test";
- }
- };
-
- ADD_TEST_F(SeparateChainingHashTableTest, hashCode);
- ADD_TEST_F(SeparateChainingHashTableTest, all);
}
}
\ No newline at end of file
diff --git a/YASI_12/ds.SeparateChainingHashTable.test.h b/YASI_12/ds.SeparateChainingHashTable.test.h
new file mode 100644
index 0000000..9ab1b97
--- /dev/null
+++ b/YASI_12/ds.SeparateChainingHashTable.test.h
@@ -0,0 +1,189 @@
+#pragma once
+
+#if YASI_TEST_THIS_MODULE != 1
+#define YASI_TEST_THIS_MODULE 1
+#endif
+#include "ds.SeparateChainingHashTable.h"
+
+namespace yasi{
+ namespace ds{
+ class SeparateChainingHashTableTest : public yasi::Test{
+ protected:
+ typedef SeparateChainingHashTable IntHashTable;
+ typedef KVPair Pair;
+ typedef IntHashTable::BucketType BucketType;
+ typedef IntHashTable::ListType ListType;
+
+ IntHashTable h;
+ public:
+
+ void hashCode(){
+ // hashcode must be within range
+ srand((unsigned int)time(NULL));
+ for (int i = 0; i < 1000; i++){
+ int n = rand();
+ int index = h.index(n);
+ ASSERT_LT(index, h.size()) << "index(" << n << ") out of range";
+ ASSERT_GE(index, 0) << "index(" << n << ") out of range";
+ }
+ }
+
+ void all(){
+ srand((unsigned int)time(NULL));
+ int logSize = h._logSize;
+ int size = h._size;
+ ASSERT_EQ(IntHashTable::INIT_LOGSIZE, logSize) << "initial logSize not " << IntHashTable::INIT_LOGSIZE;
+ ASSERT_EQ(1 << logSize, h.size()) << "table size not 2^" << logSize;
+ for (int i = 0; i < h.size(); i++){
+ ASSERT_EQ(0, (int)h.table[i]) << "table[i] not NULL for i=" << i;
+ }
+
+ {
+ SCOPED_TRACE("add items in hashtable");
+ // now add 10 items; should not trigger grow()
+ ASSERT_LT(10 / h.size(), h.DENSITY_MAX) << "10/" << h.size() << " items triggered grow() while DENSITY_MAX=" << h.DENSITY_MAX;
+ for (int i = 0; i < 10; i++){
+ h.put(i, i);
+ }
+ // test contains()
+ for (int i = 0; i < 10; i++){
+ ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found";
+ }
+ ASSERT_EQ(false, h.contains(100)) << "absent key 100 was found";
+ // test get()
+ for (int i = 0; i < 10; i++){
+ int* pValue = h.get(i);
+ ASSERT_NE(NULL, (int)pValue) << "pValue with key " << i << " NULL";
+ ASSERT_EQ(i, *pValue) << "value with key " << i << " mismatch";
+ }
+ ASSERT_EQ(NULL, h.get(100)) << "absent key 100 was found";
+ // test duplicate insert
+ // should result in update
+ h.put(5, -6);
+ int* pValue = h.get(5);
+ ASSERT_NE(NULL, (int)pValue) << "pValue with key " << 5 << " NULL";
+ ASSERT_EQ(-6, *pValue) << "value with key " << 5 << " not -6";
+
+ // now add some more but don't trigger grow()
+ size = h.size();
+ int maxCurrent = ((int)(h.size() * h.DENSITY_MAX)) - 1;
+ int currentPop = h.population();
+ for (int i = currentPop; i < maxCurrent; i++){
+ // this insertion should not trigger grow()
+ h.put(i, i);
+ }
+ ASSERT_EQ(maxCurrent, h.population()) << "population not maxCurrent";
+ ASSERT_EQ(size, h.size()) << "size() not size";
+ // this insertion should trigger grow()
+ int key = rand();
+ while (h.contains(key)){ key = rand(); }
+ h.put(key, key); // should trigger grow()
+ ASSERT_EQ(size * 2, h.size()) << "size() not 2*oldSize";
+ ASSERT_EQ(maxCurrent + 1, h.population()) << "population() not maxCurrent+1";
+ ASSERT_GE(0.375, h.density()) << "density() > 0.375";
+
+ // print the table
+ string str = h.toString();
+ cout << "after grow(): " << endl << str << endl;
+
+ // check that all old entries are still in the table
+ for (int i = 0; i < 10; i++){ // first 10 entries
+ int v = i;
+ if (i == 5){
+ // remember that we had updated an entry
+ v = -6;
+ }
+ ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found in new table";
+ ASSERT_EQ(v, *h.get(i)) << "value with key " << i << " incorrect in new table";
+ }
+ for (int i = currentPop; i < maxCurrent; i++){ // further entries till max capacity before grow
+ ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found in new table";
+ ASSERT_EQ(i, *h.get(i)) << "value with key " << i << " incorrect in new table";
+ }
+ // the entry that triggered grow()
+ ASSERT_EQ(true, h.contains(key)) << "key " << key << " not found in new table";
+ ASSERT_EQ(key, *h.get(key)) << " value with key " << key << " incorrect in new table";
+ }
+
+ {
+ SCOPED_TRACE("remove");
+ // now remove entries but do not trigger shrink()
+ int i = 0;
+ BucketType pCriticalBucket = NULL;
+ DoublyLinkedList survivedKeys;
+ int initPop = h.population();
+ int numRemoved = 0;
+ bool removedEnough = false;
+ for (; i < h.size(); i++){
+ BucketType pList = h.table[i];
+ if (pList){
+ // number of entries touched: either removed or copied
+ int remainingItems = pList->size();
+
+ while (remainingItems > 0){
+ // check if we can remove this node without causing shrink()
+ if (!removedEnough &&
+ ((float)(h._population - 1)) / h._size <= h.DENSITY_MIN){
+ // this deletion will cause shrink()
+ pCriticalBucket = pList;
+ removedEnough = true;
+ }
+ else{
+ Pair kvPair = (*pList->begin());
+ int key = kvPair.key;
+ if (removedEnough == false){
+ // remove an entry
+ int prevPop = h._population;
+ int prevSize = h.size();
+ h.remove(key);
+ ASSERT_EQ(prevPop - 1, h._population) << "remove did not work";
+ ASSERT_EQ(h._size, prevSize) << "size changed by remove";
+ ASSERT_EQ(false, h.contains(key)) << "removed key " << key << " still remains";
+ numRemoved++;
+ remainingItems--;
+ }
+ else{
+ // copy this should-be-surviving entry into a list for further verification
+ survivedKeys.push(kvPair);
+ remainingItems--;
+ }
+ }
+ }
+ }
+ } // for loop through all slots
+
+ ASSERT_EQ(initPop - numRemoved, survivedKeys.size()) << "initSize+numRemoved not survivedKeys.size";
+ if (removedEnough) {
+ // ok; removing next key should cause shrink()
+ int prevPop = h._population;
+ int prevSize = h.size();
+ int removedKey = (*pCriticalBucket->begin()).key;
+ h.remove(removedKey);
+ cout << "shrinked: \n" << h.toString() << endl;
+ ASSERT_EQ(h._population, prevPop - 1) << "remove did not work";
+ ASSERT_EQ(h._size, prevSize >> 1) << "size did not shrink by half";
+ ASSERT_EQ(false, h.contains(removedKey)) << "removed key " << removedKey << " still remains";
+
+ // now check that all should-have-survived keys are still present in the new table
+ for (DoublyLinkedList::iterator it = survivedKeys.begin(); it != survivedKeys.end(); it++){
+ int key = (*it).key;
+ if (key == removedKey) continue; // this is the removed key that made the table shrink
+ int value = (*it).value;
+ ASSERT_EQ(true, h.contains(key)) << "key " << key << " absent in shrinked table";
+ ASSERT_NE(NULL, (int)h.get(key)) << "get(" << key << ") is NULL in shrinked table";
+ ASSERT_EQ(value, *(h.get(key))) << "get(" << key << ") not " << value << "in shrinked table";
+ }
+
+ }
+ }
+
+
+ //ASSERT_EQ(0, 1) << "incomplete test";
+ }
+ };
+
+ ADD_TEST_F(SeparateChainingHashTableTest, hashCode);
+ ADD_TEST_F(SeparateChainingHashTableTest, all);
+
+ }
+}
\ No newline at end of file
diff --git a/YASI_12/ds.hashtablebase.h b/YASI_12/ds.hashtablebase.h
index 5b9f13a..b6a77df 100644
--- a/YASI_12/ds.hashtablebase.h
+++ b/YASI_12/ds.hashtablebase.h
@@ -41,6 +41,52 @@ class IntHashFunction : public IHashFunction{
}
};
+// various simplistic hash functions for testing
+namespace testhash{
+ template
+ class IdentityFunction : public IHashFunction < Key > {
+ public:
+ virtual int operator()(const Key& n) const override{
+ return n;
+ }
+ };
+ template
+ class Mod8Function : public IHashFunction < Key > {
+ public:
+ static const unsigned int modulus = 8;
+ virtual int operator()(const Key& n) const override{
+ return n % 8;
+ }
+ };
+ template
+ class Mod16Function : public IHashFunction < Key > {
+ public:
+ static const unsigned int modulus = 16;
+ virtual int operator()(const Key& n) const override{
+ return n % 16;
+ }
+ };
+ template
+ class Mod32Function : public IHashFunction < Key > {
+ public:
+ static const unsigned int modulus = 32;
+ virtual int operator()(const Key& n) const override{
+ return n % 32;
+ }
+ };
+ // mod 16 by default
+ template
+ class ModFunction : public IHashFunction < Key > {
+ public:
+ unsigned int modulus;
+ ModFunction() : modulus(16){}
+ virtual int operator()(const Key& n) const override{
+ return n % modulus;
+ }
+ };
+
+} // namespace testhash
+
//template
//class ICollisionStrategy{
diff --git a/YASI_12/main.cpp b/YASI_12/main.cpp
index e3628ed..163949a 100644
--- a/YASI_12/main.cpp
+++ b/YASI_12/main.cpp
@@ -1,7 +1,5 @@
#include "common.h"
-//#include "ds.BSTDictionary.h"
-//#include "ds.separatechaininghashtable.h"
//#include "ds.intlinearprobinghashtable.h"
//#include "ds.linearprobinghashtable.h"
//#include "ds.hopscotchhashtable.h"
@@ -31,6 +29,10 @@ using namespace std;
#include "ds.priorityqueue.test.h"
#include "ds.mutablepriorityqueue.test.h"
#include "ds.bstdictionary.test.h"
+#include "ds.separatechaininghashtable.test.h"
+#include "ds.linearprobinghashtable.test.h"
+#include "ds.intlinearprobinghashtable.test.h"
+#include "ds.HopScotchHashTable.test.h"
int testAll(int* argc, char** argv){