Skip to content

Commit

Permalink
feat(add): support std::hash (#19)
Browse files Browse the repository at this point in the history
* update test files' name; add support hash for i128 and u128

* update hash function; add an example of hash

* add tests for hash of i128 and u128

* add desc for the example of hash
  • Loading branch information
guuzaa authored Mar 15, 2024
1 parent 84d563f commit b8124e9
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 6 deletions.
23 changes: 23 additions & 0 deletions examples/hash.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "numbers.h"

int main(int argc, char const *argv[]) {
{
std::cout << "==== hash example for i128 ==== \n";
std::hash<numbers::i128> hasher;
numbers::i128 a = 9;
std::cout << "a= " << a << ", hash(a)= " << hasher(a) << '\n';
numbers::i128 b = 10;
std::cout << "b= " << b << ", hash(b)= " << hasher(b) << '\n';
numbers::i128 c = 8526517779554707146;
std::cout << "c= " << c << ", hash(c)= " << hasher(c) << '\n';
}

{
std::cout << "==== hash example for u128 ==== \n";
std::hash<numbers::u128> hasher;
numbers::u128 a = 9;
std::cout << "a= " << a << ", hash(a)= " << hasher(a) << '\n';
numbers::u128 b = 8526517779554707146;
std::cout << "b= " << b << ", hash(b)= " << hasher(b) << '\n';
}
}
18 changes: 18 additions & 0 deletions examples/size.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "numbers.h"

int main(int argc, char const *argv[])
{
std::cout << "==== size example ==== \n";
std::cout << "sizeof(numbers::i8) = "<< sizeof(numbers::i8) << '\n';
std::cout << "sizeof(numbers::i16) = "<< sizeof(numbers::i16) << '\n';
std::cout << "sizeof(numbers::i32) = "<< sizeof(numbers::i32) << '\n';
std::cout << "sizeof(numbers::i64) = "<< sizeof(numbers::i64) << '\n';
std::cout << "sizeof(numbers::i128) = "<< sizeof(numbers::i128) << '\n';

std::cout << "sizeof(numbers::u8) = "<< sizeof(numbers::u8) << '\n';
std::cout << "sizeof(numbers::u16) = "<< sizeof(numbers::u16) << '\n';
std::cout << "sizeof(numbers::u32) = "<< sizeof(numbers::u32) << '\n';
std::cout << "sizeof(numbers::u64) = "<< sizeof(numbers::u64) << '\n';
std::cout << "sizeof(numbers::u128) = "<< sizeof(numbers::u128) << '\n';
return 0;
}
14 changes: 14 additions & 0 deletions src/include/int128.hh
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ constexpr uint128 uint128_max() {

// Specialized numeric_limits for uint128.
namespace std {
template <>
struct hash<numbers::uint128> {
size_t operator()(const numbers::uint128 &obj) const {
return (std::hash<uint64_t>()(uint128_high64(obj)) << 1) ^ std::hash<uint64_t>()(uint128_low64(obj));
}
};

template <>
class numeric_limits<numbers::uint128> {
public:
Expand Down Expand Up @@ -310,6 +317,13 @@ namespace std {
template <>
struct is_signed<numbers::int128> : std::true_type {};

template <>
struct hash<numbers::int128> {
size_t operator()(const numbers::int128 &obj) const {
return std::hash<int64_t>()(int128_high64(obj) << 1) ^ std::hash<uint64_t>()(int128_low64(obj));
}
};

template <>
class numeric_limits<numbers::int128> {
public:
Expand Down
33 changes: 31 additions & 2 deletions src/include/integer.hh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#ifndef HEADER_INTEGER_H
#define HEADER_INTEGER_H
#ifndef NUMBERS_INTEGER_HH
#define NUMBERS_INTEGER_HH

#include <iostream>
#include <limits>
Expand Down Expand Up @@ -409,4 +409,33 @@ using i128 = Integer<int128>;

} // namespace numbers

namespace std {
template <>
struct hash<numbers::i8> {
size_t operator()(const numbers::i8 &obj) const { return std::hash<int8_t>()(static_cast<int8_t>(obj)); }
};

template <>
struct hash<numbers::i16> {
size_t operator()(const numbers::i16 &obj) const { return std::hash<int16_t>()(static_cast<int16_t>(obj)); }
};

template <>
struct hash<numbers::i32> {
size_t operator()(const numbers::i32 &obj) const { return std::hash<int32_t>()(static_cast<int32_t>(obj)); }
};

template <>
struct hash<numbers::i64> {
size_t operator()(const numbers::i64 &obj) const { return std::hash<int64_t>()(static_cast<int64_t>(obj)); }
};

// TODO hash collision
template <>
struct hash<numbers::i128> {
size_t operator()(const numbers::i128 &obj) const { return std::hash<numbers::int128>()(static_cast<numbers::int128>(obj)); }
};

} // namespace std

#endif
33 changes: 31 additions & 2 deletions src/include/uinteger.hh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#ifndef HEADER_UNINTEGER_H
#define HEADER_UNINTEGER_H
#ifndef NUMBERS_UNINTEGER_HH
#define NUMBERS_UNINTEGER_HH

#include <iostream>
#include <limits>
Expand Down Expand Up @@ -348,4 +348,33 @@ using u128 = Uinteger<uint128>;

} // namespace numbers

namespace std {
template <>
struct hash<numbers::u8> {
size_t operator()(const numbers::u8 &obj) const { return std::hash<uint8_t>()(static_cast<uint8_t>(obj)); }
};

template <>
struct hash<numbers::u16> {
size_t operator()(const numbers::u16 &obj) const { return std::hash<uint16_t>()(static_cast<uint16_t>(obj)); }
};

template <>
struct hash<numbers::u32> {
size_t operator()(const numbers::u32 &obj) const { return std::hash<uint32_t>()(static_cast<uint32_t>(obj)); }
};

template <>
struct hash<numbers::u64> {
size_t operator()(const numbers::u64 &obj) const { return std::hash<uint64_t>()(static_cast<uint64_t>(obj)); }
};

// TODO hash collision
template <>
struct hash<numbers::u128> {
size_t operator()(const numbers::u128 &obj) const { return std::hash<numbers::uint128>()(static_cast<numbers::uint128>(obj)); }
};

} // namespace std

#endif
2 changes: 1 addition & 1 deletion tests/integer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*test.cc")
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*.test.cc")

set(PROJECT_NAME integer_test)

Expand Down
File renamed without changes.
11 changes: 11 additions & 0 deletions tests/integer/int128_test.cc → tests/integer/int128.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,14 @@ TEST(Int128Test, NumericLimits) {
EXPECT_EQ(int128_min(), std::numeric_limits<int128>::lowest());
EXPECT_EQ(int128_max(), std::numeric_limits<int128>::max());
}

TEST(Int128Test, Hash) {
int128 a = make_int128(34, 56);
int128 b = a;
std::hash<int128> hasher;

EXPECT_EQ(hasher(a), hasher(a));
EXPECT_EQ(hasher(b), hasher(b));

EXPECT_EQ(hasher(a), hasher(b));
}
23 changes: 23 additions & 0 deletions tests/integer/integer_test.cc → tests/integer/integer.test.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <numeric>
#include <unordered_set>
#include <vector>
#include "gtest/gtest.h"

Expand Down Expand Up @@ -1128,3 +1129,25 @@ TEST(integerTest, integerSaturatingNegNoSideEffects) {
auto _ = num.saturating_neg();
ASSERT_EQ(num, tmp_num);
}

TEST(integerTest, Hash) {
i64 a = 4567983;
i64 b = a;
std::hash<i64> hasher;

EXPECT_EQ(hasher(a), hasher(a));
EXPECT_EQ(hasher(b), hasher(b));

EXPECT_EQ(hasher(a), hasher(b));
}

TEST(integerTest, UnorderedSet) {
std::unordered_set<i8> s8;
size_t cnt = 0;
for (i8 i = 0; i < i8::MAX; i = i.saturating_add(17)) {
s8.insert(i);
++cnt;
ASSERT_EQ(s8.size(), cnt);
ASSERT_EQ(s8.count(i), 1);
}
}
2 changes: 1 addition & 1 deletion tests/uinteger/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*test.cc")
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*.test.cc")

set(PROJECT_NAME uinteger_test)

Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions tests/uinteger/uint128_test.cc → tests/uinteger/uint128.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -392,4 +392,17 @@ TEST_F(Uint128Test, NumericLimits) {
EXPECT_EQ(std::numeric_limits<numbers::uint128>::round_style, std::round_toward_zero);
}

TEST_F(Uint128Test, Hash) {
using namespace numbers;

uint128 a = make_uint128(56, 78);
uint128 b = a;
std::hash<uint128> hasher;

EXPECT_EQ(hasher(a), hasher(a));
EXPECT_EQ(hasher(b), hasher(b));

EXPECT_EQ(hasher(a), hasher(b));
}

} // namespace
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "gtest/gtest.h"

#include <unordered_set>
#include "test/utils.hh"
#include "uinteger.hh"

Expand Down Expand Up @@ -769,3 +770,25 @@ TEST(UintegerTest, UintegerWrappingNegNoSideEffects) {
auto _ = num.wrapping_neg();
ASSERT_EQ(num, tmp_num);
}

TEST(UintegerTest, Hash) {
u32 a = 1245679;
u32 b = a;
std::hash<u32> hasher;

EXPECT_EQ(hasher(a), hasher(a));
EXPECT_EQ(hasher(b), hasher(b));

EXPECT_EQ(hasher(a), hasher(b));
}

TEST(UintegerTest, UnorderedSet) {
std::unordered_set<u8> s8;
size_t cnt = 0;
for (u8 i = u8::MIN; i < u8::MAX; i = i.saturating_add(19)) {
s8.insert(i);
++cnt;
ASSERT_EQ(s8.size(), cnt);
ASSERT_EQ(s8.count(i), 1);
}
}

0 comments on commit b8124e9

Please sign in to comment.