diff --git a/src/hashing.h b/src/hashing.h
index 3551d6862151aab80ea1600dabeecb5d74f655b9..5773504962e0a61c523cb366de12cb76dba66c53 100644
--- a/src/hashing.h
+++ b/src/hashing.h
@@ -8,56 +8,58 @@
  * hashing by division
  */
 template<typename T>
-struct DivHashing {
-	const size_t m;
-	DivHashing(size_t table_size) : m(table_size) {}
-
-	size_t operator()(T key) { return key % m; }
+class DivHashing {
+	public:
+		DivHashing(size_t table_size) : m(table_size) {}
+		size_t operator()(T key) const { return key % m; }
+	private:
+		size_t m;
 };
 
 /*
  * hashing by multiplication
  */
 template<typename T>
-struct MulHashing {
-	const double gratio = 0.61803;
-	const size_t m;
-	MulHashing(size_t table_size) : m(table_size) {}
-
-	size_t operator()(T key) { return m * (gratio*key - std::floor(gratio*key)); }
+class MulHashing {
+	public:
+		MulHashing(size_t table_size) : m(table_size) {}
+		size_t operator()(T key) { return m * (gratio*key - std::floor(gratio*key)); }
+	private:
+		double gratio = 0.61803;
+		size_t m;
 };
 
 /*
  * linear probing
  */
-struct LinearProbing {
-	const size_t h;
-	const size_t m;
-	LinearProbing(size_t hash, size_t table_size) : h(hash), m(table_size) {}
-
-	size_t operator()(size_t i) { return (h+i) % m; }
+class LinearProbing {
+	public:
+		LinearProbing(size_t table_size) : m(table_size) {}
+		size_t operator()(size_t h, size_t i) const { return (h+i) % m; }
+	private:
+		size_t m;
 };
 
 /*
  * quadratic probing
  */
-struct QuadraticProbing {
-	const size_t h;
-	const size_t m;
-	QuadraticProbing(size_t hash, size_t table_size) : h(hash), m(table_size) {}
-
-	size_t operator()(size_t i) { return (h + i*i) % m; }
+class QuadraticProbing {
+	public:
+		QuadraticProbing(size_t table_size) : m(table_size) {}
+		size_t operator()(size_t h, size_t i) { return (h + i*i) % m; }
+	private:
+		size_t m;
 };
 
 /*
  * double hashing
  */
-struct DoubleHashing {
-	const size_t h;
-	const size_t m;
-	DoubleHashing(size_t hash, size_t table_size) : h(hash), m(table_size) {}
-
-	size_t operator()(size_t i) { return (h + i*(h+1)) % m; }
+class DoubleHashing {
+	public:
+		DoubleHashing(size_t table_size) : m(table_size) {}
+		size_t operator()(size_t h, size_t i) { return (h + i*(2*h+1)) % m; }
+	private:
+		size_t m;
 };
 
 #endif
diff --git a/src/hashtable.cpp b/src/hashtable.cpp
index 55c47d839ebbc669ad76481f924f198869785e0d..ea7d00acd5fa24c8a5db6134219f7285447873a7 100644
--- a/src/hashtable.cpp
+++ b/src/hashtable.cpp
@@ -1,26 +1,23 @@
 #include <cmath>
-#include <cassert>
-#include <iostream>
 #include <stdexcept>
 
 #include "hashtable.h"
 
 
-template <typename T>
-Hashtable<T>::Hashtable(size_t m, float alpha, float s):
-	slot_count(m), max_loadfactor(alpha), growfactor(s), table(new Slot<T>[m])
+template <typename T, typename H, typename P>
+Hashtable<T,H,P>::Hashtable(size_t m, float alpha, float s):
+	slot_count(m), max_loadfactor(alpha), growfactor(s), table(new Slot<T>[m]),
+	hash(H(m)), probe(P(m))
 {
-
 	if (!(0 < alpha && alpha < 1))
 		throw std::invalid_argument("load factor alpha must be in range (0;1)");
 	if (!(s > 1))
 		throw std::invalid_argument("grow factor s must be greater 1");
 }
 
-template <typename T>
-bool Hashtable<T>::insert(T key) {
+template <typename T, typename H, typename P>
+bool Hashtable<T,H,P>::insert(T key) {
 	// Grow the table if inserting would exceed the load factor.
-	// If the key is already in the table this could grow the table unnecessarily once.
 	if (slot_count == 0 || (element_count+1.0f)/slot_count > max_loadfactor)
 		grow();
 
@@ -42,13 +39,15 @@ bool Hashtable<T>::insert(T key) {
 		}
 	}
 
-	// If the probing strategy permutates {0,...,m-1} the for-loop
-	// visits every slot and will find an empty one eventually.
-	throw std::logic_error("will we ever reach here?");
+	// If we did not reach a "return" during the for-loop, we could not find a
+	// slot for the key. Grow the table and try again.
+	grow();
+	return insert(key);
 }
 
-template <typename T>
-bool Hashtable<T>::remove(T key) {
+
+template <typename T, typename H, typename P>
+bool Hashtable<T,H,P>::remove(T key) {
 	if (slot_count == 0)
 		return false; // table is empty
 
@@ -58,6 +57,7 @@ bool Hashtable<T>::remove(T key) {
 
 		if (slot.label == Slot<T>::vacant) {
 			// found a vacant slot: key is not in table
+			return false;
 			break;
 		}
 
@@ -72,8 +72,8 @@ bool Hashtable<T>::remove(T key) {
 	return false;
 }
 
-template <typename T>
-bool Hashtable<T>::contains(T key) {
+template <typename T, typename H, typename P>
+bool Hashtable<T,H,P>::contains(T key) {
 	if (slot_count == 0)
 		return false; // table is empty
 
@@ -87,6 +87,7 @@ bool Hashtable<T>::contains(T key) {
 		}
 
 		if (slot.label == Slot<T>::used && slot.key == key) {
+			// found the key
 			return true;
 		}
 	}
@@ -94,73 +95,96 @@ bool Hashtable<T>::contains(T key) {
 	return false;
 }
 
-template <typename T>
-float Hashtable<T>::load_factor() const {
+template <typename T, typename H, typename P>
+float Hashtable<T,H,P>::load_factor() const {
 	return slot_count == 0 ? 0 : static_cast<float>(element_count)/slot_count;
 }
 
-template <typename T>
-inline size_t Hashtable<T>::hash(T key) {
-	assert(slot_count != 0);
-	return key % slot_count;
-}
+template <typename T, typename H, typename P>
+void Hashtable<T,H,P>::grow() {
+	size_t new_capacity = std::ceil(growfactor * (slot_count==0 ? 1 : slot_count));
 
-template <typename T>
-inline size_t Hashtable<T>::probe(size_t h, size_t i) {
-	assert(slot_count != 0);
-	return (h+i) % slot_count;
-}
+	while (true) {
+		// allocate space for new table
+		Slot<T> *new_table = new Slot<T>[new_capacity];
+
+		// new table, new hash/probe functions
+		auto new_hash = H(new_capacity);
+		auto new_probe = P(new_capacity);
 
-template <typename T>
-void Hashtable<T>::grow() {
-	size_t old_capacity = slot_count;
-	slot_count = std::ceil(growfactor * (slot_count==0 ? 1 : slot_count));
-	Slot<T> *const new_table = new Slot<T>[slot_count];
+		// keeps track of the number of inserts into the new table
+		size_t insert_count = 0;
 
-	// for all slots in the old table ...
-	for (size_t i=0; i<old_capacity; i++) {
-		Slot<T> &os = table[i];
+		// for all slots in the old table ...
+		for (size_t i=0; i<slot_count; i++) {
+			Slot<T> &os = table[i];
 
-		// that store a key ...
-		if (os.label == Slot<T>::used) {
-			unsigned hk = hash(os.key);
+			// that currently store a key ...
+			if (os.label == Slot<T>::used) {
+				size_t hk = new_hash(os.key);
 
-			// find a slot in the new table
-			for (size_t j=0; j<slot_count; j++) {
-				Slot<T> &ns = new_table[probe(hk, j)];
+				// find a slot in the new table ...
+				for (size_t j=0; j<new_capacity; j++) {
+					Slot<T> &ns = new_table[new_probe(hk, j)];
 
-				if (ns.label == Slot<T>::vacant) {
-					ns.key = os.key;
-					ns.label = Slot<T>::used;
-					break;
+					// and insert the key
+					if (ns.label == Slot<T>::vacant) {
+						ns.key = os.key;
+						ns.label = Slot<T>::used;
+						insert_count++;
+						break;
+					}
 				}
 			}
 		}
-	}
 
-	table.reset(new_table); // smart_ptr frees the old table
+		// It is possible that not all keys were transferred into the new table.
+		// This can happen if the probing strategy does not try all/enough slots
+		// (e.g. quadratic/double hashing with bad table sizes (not prime) or
+		// high load factor/small grow factor combinations).
+		// In such a case we grow the table again.
+		if (insert_count != element_count) {
+			new_capacity = std::ceil(growfactor*new_capacity);
+			delete[] new_table;
+			continue;
+		}
+
+		slot_count = new_capacity;
+		hash = new_hash;
+		probe = new_probe;
+		table.reset(new_table); // smart_ptr frees the old table
+		return;
+	}
 }
 
-template <typename T>
-void Hashtable<T>::info() const {
-	std::cerr << "size: " << size() << ", capacity: " << capacity()
-		<< ", alpha (current/max): " << load_factor() << "/" << max_loadfactor << std::endl;
-	std::cerr << "array size: " << slot_count*sizeof(Slot<T>)/1024 << "kB, start: "
-		<< table.get() << ", end: " << table.get()+slot_count << std::endl;
+template <typename T, typename H, typename P>
+std::string Hashtable<T,H,P>::info() const {
+	std::string s;
+	s += "size: " + std::to_string(size());
+	s += ", capacity: " + std::to_string(capacity());
+	s += ", alpha: " + std::to_string(load_factor());
+
+	return s;
 }
 
-template <typename T>
-void Hashtable<T>::print() const {
+template <typename T, typename H, typename P>
+std::string Hashtable<T,H,P>::print() const {
+	std::string s;
+
 	for (size_t i=0; i<slot_count; i+=10) {
-		std::cerr << i << ": ";
+		s += std::to_string(i) + ": ";
+
 		for (size_t j=i; j<i+10 && j<slot_count; j++) {
-			const Slot<T> &s = table[j];
 			std::string repr;
-			if (s.label == Slot<T>::vacant) repr = "F";
-			else if(s.label == Slot<T>::deleted) repr = "D";
-			else repr = std::to_string(s.key);
-			std::cerr << repr << "  ";
+
+			if (table[j].label == Slot<T>::vacant) repr = "F ";
+			else if(table[j].label == Slot<T>::deleted) repr = "D ";
+			else repr = std::to_string(table[j].key) + " ";
+			s += repr;
 		}
-		std::cerr << std::endl;
+
+		s += "\n";
 	}
+
+	return s;
 }
diff --git a/src/hashtable.h b/src/hashtable.h
index a61e90191699c9e8de1a28fddbde264d6cc4c24c..1d394a99bae945f7f904c00cab22eaf31365016d 100644
--- a/src/hashtable.h
+++ b/src/hashtable.h
@@ -1,21 +1,17 @@
 #ifndef HASHTABLE_H
 #define HASHTABLE_H
 
+#include <string>
 #include <memory>
 
+#include "hashing.h"
 
-template<typename T> class Hashtable;
 
-/* a bucket of size 1 */
-template <typename T>
-class Slot {
-	T key;
-	enum: char { vacant, deleted, used } label = vacant;
+template<typename T, typename H, typename P> class Hashtable;
+template<typename T> class Slot;
 
-	friend Hashtable<T>;
-};
 
-template <typename T>
+template <typename T, typename H = DivHashing<T>, typename P = LinearProbing>
 class Hashtable {
 	public:
 		Hashtable(size_t m=8, float alpha=0.75, float s=2.0);
@@ -27,8 +23,8 @@ class Hashtable {
 		size_t capacity() const { return slot_count; }
 		float load_factor() const;
 
-		void info() const;
-		void print() const;
+		std::string info() const;
+		std::string print() const;
 	private:
 		size_t element_count = 0;
 		size_t slot_count;
@@ -36,16 +32,28 @@ class Hashtable {
 		const float growfactor;
 		std::unique_ptr<Slot<T>[]> table;
 
-		size_t hash(T key);
-		size_t probe(size_t h, size_t i);
+		H hash;
+		P probe;
+
 		void grow();
 };
 
+/* a bucket of size 1 */
+template <typename T>
+class Slot {
+	T key;
+	enum: char { vacant, deleted, used } label = vacant;
+
+	template <typename TT, typename H, typename P> friend class Hashtable;
+};
+
 // declare some template instantiations explicitly
 // so they are included in the shared library
-template class Hashtable<unsigned int>;
-template class Hashtable<unsigned long>;
-template class Hashtable<unsigned short>;
-template class Hashtable<unsigned char>;
+template class Hashtable<unsigned int, DivHashing<unsigned int>, LinearProbing>;
+template class Hashtable<unsigned int, DivHashing<unsigned int>, QuadraticProbing>;
+template class Hashtable<unsigned int, DivHashing<unsigned int>, DoubleHashing>;
+template class Hashtable<unsigned int, MulHashing<unsigned int>, LinearProbing>;
+template class Hashtable<unsigned int, MulHashing<unsigned int>, QuadraticProbing>;
+template class Hashtable<unsigned int, MulHashing<unsigned int>, DoubleHashing>;
 
 #endif
diff --git a/tests/test_hashing.cpp b/tests/test_hashing.cpp
index 719b9588e121fe9102b4ddb0e3ebd6d6d340c0b4..45f17104df280e8b9b678c852c3cefeb0451660a 100644
--- a/tests/test_hashing.cpp
+++ b/tests/test_hashing.cpp
@@ -39,64 +39,64 @@ TEST(hashing, MulHashing) {
 }
 
 TEST(hashing, LinearProbing) {
-	LinearProbing p0(2, 1);
-	LinearProbing p1(4, 512);
-	LinearProbing p2(0, 8);
-	LinearProbing p3(7, 32);
-	LinearProbing p4(16, 6899);
+	LinearProbing p0(1);
+	LinearProbing p1(512);
+	LinearProbing p2(8);
+	LinearProbing p3(32);
+	LinearProbing p4(6899);
 
 	for (unsigned i=0; i<10000; i++) {
-		EXPECT_LT(p0(i), 1);
-		EXPECT_EQ(p0(i), (2+i)%1);
-		EXPECT_LT(p1(i), 512);
-		EXPECT_EQ(p1(i), (4+i)%512);
-		EXPECT_LT(p2(i), 8);
-		EXPECT_EQ(p2(i), (0+i)%8);
-		EXPECT_LT(p3(i), 32);
-		EXPECT_EQ(p3(i), (7+i)%32);
-		EXPECT_LT(p4(i), 6899);
-		EXPECT_EQ(p4(i), (16+i)%6899);
+		EXPECT_LT(p0(2,i), 1);
+		EXPECT_EQ(p0(2,i), (2+i)%1);
+		EXPECT_LT(p1(4,i), 512);
+		EXPECT_EQ(p1(4,i), (4+i)%512);
+		EXPECT_LT(p2(0,i), 8);
+		EXPECT_EQ(p2(0,i), (0+i)%8);
+		EXPECT_LT(p3(7,i), 32);
+		EXPECT_EQ(p3(7,i), (7+i)%32);
+		EXPECT_LT(p4(16,i), 6899);
+		EXPECT_EQ(p4(16,i), (16+i)%6899);
 	}
 }
 
 TEST(hashing, QuadraticProbing) {
-	QuadraticProbing p0(2, 1);
-	QuadraticProbing p1(4, 512);
-	QuadraticProbing p2(0, 8);
-	QuadraticProbing p3(7, 32);
-	QuadraticProbing p4(16, 6899);
+	QuadraticProbing p0(1);
+	QuadraticProbing p1(512);
+	QuadraticProbing p2(8);
+	QuadraticProbing p3(32);
+	QuadraticProbing p4(6899);
 
 	for (unsigned i=0; i<10000; i++) {
-		EXPECT_LT(p0(i), 1);
-		EXPECT_EQ(p0(i), (2+i*i)%1);
-		EXPECT_LT(p1(i), 512);
-		EXPECT_EQ(p1(i), (4+i*i)%512);
-		EXPECT_LT(p2(i), 8);
-		EXPECT_EQ(p2(i), (0+i*i)%8);
-		EXPECT_LT(p3(i), 32);
-		EXPECT_EQ(p3(i), (7+i*i)%32);
-		EXPECT_LT(p4(i), 6899);
-		EXPECT_EQ(p4(i), (16+i*i)%6899);
+		EXPECT_LT(p0(2,i), 1);
+		EXPECT_EQ(p0(2,i), (2+i*i)%1);
+		EXPECT_LT(p1(4,i), 512);
+		EXPECT_EQ(p1(4,i), (4+i*i)%512);
+		EXPECT_LT(p2(0,i), 8);
+		EXPECT_EQ(p2(0,i), (0+i*i)%8);
+		EXPECT_LT(p3(7,i), 32);
+		EXPECT_EQ(p3(7,i), (7+i*i)%32);
+		EXPECT_LT(p4(16,i), 6899);
+		EXPECT_EQ(p4(16,i), (16+i*i)%6899);
 	}
 }
 
 TEST(hashing, DoubleHashing) {
-	DoubleHashing p0(2, 1);
-	DoubleHashing p1(4, 512);
-	DoubleHashing p2(0, 8);
-	DoubleHashing p3(7, 32);
-	DoubleHashing p4(16, 6899);
+	DoubleHashing p0(1);
+	DoubleHashing p1(512);
+	DoubleHashing p2(8);
+	DoubleHashing p3(32);
+	DoubleHashing p4(6899);
 
 	for (unsigned i=0; i<10000; i++) {
-		EXPECT_LT(p0(i), 1);
-		EXPECT_EQ(p0(i), (2+i*(2+1))%1);
-		EXPECT_LT(p1(i), 512);
-		EXPECT_EQ(p1(i), (4+i*(4+1))%512);
-		EXPECT_LT(p2(i), 8);
-		EXPECT_EQ(p2(i), (0+i*(0+1))%8);
-		EXPECT_LT(p3(i), 32);
-		EXPECT_EQ(p3(i), (7+i*(7+1))%32);
-		EXPECT_LT(p4(i), 6899);
-		EXPECT_EQ(p4(i), (16+i*(16+1))%6899);
+		EXPECT_LT(p0(2,i), 1);
+		EXPECT_EQ(p0(2,i), (2+i*(2*2+1))%1);
+		EXPECT_LT(p1(4,i), 512);
+		EXPECT_EQ(p1(4,i), (4+i*(2*4+1))%512);
+		EXPECT_LT(p2(0,i), 8);
+		EXPECT_EQ(p2(0,i), (0+i*(2*0+1))%8);
+		EXPECT_LT(p3(7,i), 32);
+		EXPECT_EQ(p3(7,i), (7+i*(2*7+1))%32);
+		EXPECT_LT(p4(16,i), 6899);
+		EXPECT_EQ(p4(16,i), (16+i*(2*16+1))%6899);
 	}
 }
diff --git a/tests/test_hashtable.cpp b/tests/test_hashtable.cpp
index a21c0a2b16717d589aa1a779c2136bbacc0c6ce7..ff45f8467c5fd8db01d3e059821bc121e1d918e7 100644
--- a/tests/test_hashtable.cpp
+++ b/tests/test_hashtable.cpp
@@ -1,63 +1,102 @@
 #include <gtest/gtest.h>
-#include <iostream>
-#include <utility>
+#include <cstdlib>
+#include <tuple>
 
 #include "hashtable.h"
 #include "argumentTypes.h"
 
-using std::pair;
+using std::tuple;
 
 
 template<typename T>
 class HashtableTest : public testing::Test {
 	public:
-		using keytype = typename T::first_type;
-		using argtype = typename T::second_type;
+		using keytype = typename std::tuple_element<0, T>::type;
+		using hashfunc = typename std::tuple_element<1, T>::type;
+		using probefunc = typename std::tuple_element<2, T>::type;
+		using argtype = typename std::tuple_element<3, T>::type;
 
-		Hashtable<keytype> *table;
+		Hashtable<keytype,hashfunc,probefunc> *table;
 		argtype args;
 
 		HashtableTest() {
-			table = new Hashtable<keytype>(args.m, args.a, args.s);
+			table = new Hashtable<keytype,hashfunc,probefunc>(args.m, args.a, args.s);
 		}
 
 		~HashtableTest() override { delete table; }
 };
 
+/*
+ * run the tests against hashtables created with different combinations of
+ * keytype, hash function, probing strategy and constructor arguments
+ */
 using AllTheTypes = ::testing::Types<
-	pair<unsigned int,DefaultTable>,
-	pair<unsigned int,EmptyTable>,
-	pair<unsigned int,BigTable>,
-	pair<unsigned int,SmallAlphaDefaultS>,
-	pair<unsigned int,SmallAlphaTinyS>,
-	pair<unsigned int,SmallAlphaSmallS>,
-	pair<unsigned int,SmallAlphaLargeS>,
-	pair<unsigned int,LargeAlphaDefaultS>,
-	pair<unsigned int,LargeAlphaTinyS>,
-	pair<unsigned int,LargeAlphaSmallS>,
-	pair<unsigned int,LargeAlphaLargeS>,
-	pair<unsigned short,DefaultTable>,
-	pair<unsigned short,EmptyTable>,
-	pair<unsigned short,BigTable>,
-	pair<unsigned short,SmallAlphaDefaultS>,
-	pair<unsigned short,SmallAlphaTinyS>,
-	pair<unsigned short,SmallAlphaSmallS>,
-	pair<unsigned short,SmallAlphaLargeS>,
-	pair<unsigned short,LargeAlphaDefaultS>,
-	pair<unsigned short,LargeAlphaTinyS>,
-	pair<unsigned short,LargeAlphaSmallS>,
-	pair<unsigned short,LargeAlphaLargeS>,
-	pair<unsigned long,DefaultTable>,
-	pair<unsigned long,EmptyTable>,
-	pair<unsigned long,BigTable>,
-	pair<unsigned long,SmallAlphaDefaultS>,
-	pair<unsigned long,SmallAlphaTinyS>,
-	pair<unsigned long,SmallAlphaSmallS>,
-	pair<unsigned long,SmallAlphaLargeS>,
-	pair<unsigned long,LargeAlphaDefaultS>,
-	pair<unsigned long,LargeAlphaTinyS>,
-	pair<unsigned long,LargeAlphaSmallS>,
-	pair<unsigned long,LargeAlphaLargeS>
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,DefaultTable>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,EmptyTable>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,BigTable>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,SmallAlphaDefaultS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,SmallAlphaTinyS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,SmallAlphaSmallS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,SmallAlphaLargeS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,LargeAlphaDefaultS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,LargeAlphaTinyS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,LargeAlphaSmallS>,
+	tuple<unsigned,DivHashing<unsigned>,LinearProbing,LargeAlphaLargeS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,DefaultTable>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,EmptyTable>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,BigTable>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,SmallAlphaDefaultS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,SmallAlphaTinyS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,SmallAlphaSmallS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,SmallAlphaLargeS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,LargeAlphaDefaultS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,LargeAlphaTinyS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,LargeAlphaSmallS>,
+	tuple<unsigned,DivHashing<unsigned>,QuadraticProbing,LargeAlphaLargeS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,DefaultTable>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,EmptyTable>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,BigTable>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,SmallAlphaDefaultS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,SmallAlphaTinyS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,SmallAlphaSmallS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,SmallAlphaLargeS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,LargeAlphaDefaultS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,LargeAlphaTinyS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,LargeAlphaSmallS>,
+	tuple<unsigned,DivHashing<unsigned>,DoubleHashing,LargeAlphaLargeS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,DefaultTable>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,EmptyTable>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,BigTable>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,SmallAlphaDefaultS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,SmallAlphaTinyS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,SmallAlphaSmallS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,SmallAlphaLargeS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,LargeAlphaDefaultS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,LargeAlphaTinyS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,LargeAlphaSmallS>,
+	tuple<unsigned,MulHashing<unsigned>,LinearProbing,LargeAlphaLargeS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,DefaultTable>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,EmptyTable>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,BigTable>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,SmallAlphaDefaultS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,SmallAlphaTinyS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,SmallAlphaSmallS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,SmallAlphaLargeS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,LargeAlphaDefaultS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,LargeAlphaTinyS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,LargeAlphaSmallS>,
+	tuple<unsigned,MulHashing<unsigned>,QuadraticProbing,LargeAlphaLargeS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,DefaultTable>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,EmptyTable>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,BigTable>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,SmallAlphaDefaultS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,SmallAlphaTinyS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,SmallAlphaSmallS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,SmallAlphaLargeS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,LargeAlphaDefaultS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,LargeAlphaTinyS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,LargeAlphaSmallS>,
+	tuple<unsigned,MulHashing<unsigned>,DoubleHashing,LargeAlphaLargeS>
 >;
 
 TYPED_TEST_SUITE(HashtableTest, AllTheTypes);
@@ -93,6 +132,35 @@ TYPED_TEST(HashtableTest, InsertExisting) {
 	EXPECT_EQ(false, table.insert(575));
 }
 
+TYPED_TEST(HashtableTest, InsertRandom) {
+	auto &table = *this->table;
+
+	std::srand(2342);
+	for (unsigned i=0; i<4096; i++) {
+		EXPECT_EQ(true, table.insert(std::rand()));
+	}
+
+	std::srand(2342);
+	for (unsigned i=0; i<4096; i++) {
+		EXPECT_EQ(true, table.contains(std::rand()));
+	}
+
+	std::srand(2342);
+	for (unsigned i=0; i<4096; i++) {
+		EXPECT_EQ(false, table.insert(std::rand()));
+	}
+
+	std::srand(2342);
+	for (unsigned i=0; i<4096; i++) {
+		EXPECT_EQ(true, table.remove(std::rand()));
+	}
+
+	std::srand(2342);
+	for (unsigned i=0; i<4096; i++) {
+		EXPECT_EQ(false, table.contains(std::rand()));
+	}
+}
+
 TYPED_TEST(HashtableTest, RemoveFromEmpty) {
 	auto &table = *this->table;