From ac0b3d15c20d3f870e2f3a5d6eb5cab07d9463b6 Mon Sep 17 00:00:00 2001
From: David Nieder <post@davidnieder.de>
Date: Sun, 1 Jan 2023 17:15:11 +0100
Subject: [PATCH] hashing/probing as function objects

---
 src/hashing.h          |  63 +++++++++++++++++++++++++
 tests/CMakeLists.txt   |   3 ++
 tests/test_hashing.cpp | 102 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+)
 create mode 100644 src/hashing.h
 create mode 100644 tests/test_hashing.cpp

diff --git a/src/hashing.h b/src/hashing.h
new file mode 100644
index 0000000..3551d68
--- /dev/null
+++ b/src/hashing.h
@@ -0,0 +1,63 @@
+#ifndef HASHING_H
+#define HASHING_H
+
+#include <cmath>
+
+
+/*
+ * 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; }
+};
+
+/*
+ * 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)); }
+};
+
+/*
+ * 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; }
+};
+
+/*
+ * 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; }
+};
+
+/*
+ * 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; }
+};
+
+#endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 47ef1cf..d02f383 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,12 +6,15 @@ include(CTest)
 
 # the executables that run the tests
 add_executable(test_main test_main.cpp)
+add_executable(test_hashing test_hashing.cpp)
 add_executable(test_hashtable test_constructor.cpp test_hashtable.cpp)
 
 # link needed libraries
 target_link_libraries(test_main gtest gtest_main)
+target_link_libraries(test_hashing gtest gtest_main)
 target_link_libraries(test_hashtable hashtable gtest gtest_main)
 
 # run with ctest
 add_test(NAME test_main COMMAND ${EXECUTABLE_OUTPUT_PATH}/test_main)
+add_test(NAME test_hashing COMMAND ${EXECUTABLE_OUTPUT_PATH}/test_hashing)
 add_test(NAME test_hashtable COMMAND ${EXECUTABLE_OUTPUT_PATH}/test_hashtable)
diff --git a/tests/test_hashing.cpp b/tests/test_hashing.cpp
new file mode 100644
index 0000000..719b958
--- /dev/null
+++ b/tests/test_hashing.cpp
@@ -0,0 +1,102 @@
+#include <gtest/gtest.h>
+#include "hashing.h"
+
+
+TEST(hashing, DivHashing) {
+	DivHashing<unsigned> hash0(1);
+	DivHashing<unsigned> hash1(7);
+	DivHashing<unsigned> hash2(23);
+	DivHashing<unsigned> hash3(512);
+	DivHashing<unsigned> hash4(6899);
+
+	for (unsigned i=0; i<65535; i++) {
+		EXPECT_EQ(i%1, hash0(i));
+		EXPECT_EQ(i%7, hash1(i));
+		EXPECT_EQ(i%23, hash2(i));
+		EXPECT_EQ(i%512, hash3(i));
+		EXPECT_EQ(i%6899, hash4(i));
+	}
+}
+
+TEST(hashing, MulHashing) {
+	const double a = 0.61803;
+	using std::floor;
+	#define H(k,m) (floor((m*((a*k)-floor(a*k)))))
+
+	MulHashing<unsigned> hash0(1);
+	MulHashing<unsigned> hash1(7);
+	MulHashing<unsigned> hash2(23);
+	MulHashing<unsigned> hash3(512);
+	MulHashing<unsigned> hash4(6899);
+
+	for (unsigned i=0; i<65535; i++) {
+		EXPECT_EQ(H(i,1), hash0(i));
+		EXPECT_EQ(H(i,7), hash1(i));
+		EXPECT_EQ(H(i,23), hash2(i));
+		EXPECT_EQ(H(i,512), hash3(i));
+		EXPECT_EQ(H(i,6899), hash4(i));
+	}
+}
+
+TEST(hashing, LinearProbing) {
+	LinearProbing p0(2, 1);
+	LinearProbing p1(4, 512);
+	LinearProbing p2(0, 8);
+	LinearProbing p3(7, 32);
+	LinearProbing p4(16, 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);
+	}
+}
+
+TEST(hashing, QuadraticProbing) {
+	QuadraticProbing p0(2, 1);
+	QuadraticProbing p1(4, 512);
+	QuadraticProbing p2(0, 8);
+	QuadraticProbing p3(7, 32);
+	QuadraticProbing p4(16, 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);
+	}
+}
+
+TEST(hashing, DoubleHashing) {
+	DoubleHashing p0(2, 1);
+	DoubleHashing p1(4, 512);
+	DoubleHashing p2(0, 8);
+	DoubleHashing p3(7, 32);
+	DoubleHashing p4(16, 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);
+	}
+}
-- 
GitLab