Skip to content

Commit b8124e9

Browse files
authored
feat(add): support std::hash (#19)
* 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
1 parent 84d563f commit b8124e9

13 files changed

+189
-6
lines changed

Diff for: examples/hash.cc

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "numbers.h"
2+
3+
int main(int argc, char const *argv[]) {
4+
{
5+
std::cout << "==== hash example for i128 ==== \n";
6+
std::hash<numbers::i128> hasher;
7+
numbers::i128 a = 9;
8+
std::cout << "a= " << a << ", hash(a)= " << hasher(a) << '\n';
9+
numbers::i128 b = 10;
10+
std::cout << "b= " << b << ", hash(b)= " << hasher(b) << '\n';
11+
numbers::i128 c = 8526517779554707146;
12+
std::cout << "c= " << c << ", hash(c)= " << hasher(c) << '\n';
13+
}
14+
15+
{
16+
std::cout << "==== hash example for u128 ==== \n";
17+
std::hash<numbers::u128> hasher;
18+
numbers::u128 a = 9;
19+
std::cout << "a= " << a << ", hash(a)= " << hasher(a) << '\n';
20+
numbers::u128 b = 8526517779554707146;
21+
std::cout << "b= " << b << ", hash(b)= " << hasher(b) << '\n';
22+
}
23+
}

Diff for: examples/size.cc

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include "numbers.h"
2+
3+
int main(int argc, char const *argv[])
4+
{
5+
std::cout << "==== size example ==== \n";
6+
std::cout << "sizeof(numbers::i8) = "<< sizeof(numbers::i8) << '\n';
7+
std::cout << "sizeof(numbers::i16) = "<< sizeof(numbers::i16) << '\n';
8+
std::cout << "sizeof(numbers::i32) = "<< sizeof(numbers::i32) << '\n';
9+
std::cout << "sizeof(numbers::i64) = "<< sizeof(numbers::i64) << '\n';
10+
std::cout << "sizeof(numbers::i128) = "<< sizeof(numbers::i128) << '\n';
11+
12+
std::cout << "sizeof(numbers::u8) = "<< sizeof(numbers::u8) << '\n';
13+
std::cout << "sizeof(numbers::u16) = "<< sizeof(numbers::u16) << '\n';
14+
std::cout << "sizeof(numbers::u32) = "<< sizeof(numbers::u32) << '\n';
15+
std::cout << "sizeof(numbers::u64) = "<< sizeof(numbers::u64) << '\n';
16+
std::cout << "sizeof(numbers::u128) = "<< sizeof(numbers::u128) << '\n';
17+
return 0;
18+
}

Diff for: src/include/int128.hh

+14
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ constexpr uint128 uint128_max() {
134134

135135
// Specialized numeric_limits for uint128.
136136
namespace std {
137+
template <>
138+
struct hash<numbers::uint128> {
139+
size_t operator()(const numbers::uint128 &obj) const {
140+
return (std::hash<uint64_t>()(uint128_high64(obj)) << 1) ^ std::hash<uint64_t>()(uint128_low64(obj));
141+
}
142+
};
143+
137144
template <>
138145
class numeric_limits<numbers::uint128> {
139146
public:
@@ -310,6 +317,13 @@ namespace std {
310317
template <>
311318
struct is_signed<numbers::int128> : std::true_type {};
312319

320+
template <>
321+
struct hash<numbers::int128> {
322+
size_t operator()(const numbers::int128 &obj) const {
323+
return std::hash<int64_t>()(int128_high64(obj) << 1) ^ std::hash<uint64_t>()(int128_low64(obj));
324+
}
325+
};
326+
313327
template <>
314328
class numeric_limits<numbers::int128> {
315329
public:

Diff for: src/include/integer.hh

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#ifndef HEADER_INTEGER_H
2-
#define HEADER_INTEGER_H
1+
#ifndef NUMBERS_INTEGER_HH
2+
#define NUMBERS_INTEGER_HH
33

44
#include <iostream>
55
#include <limits>
@@ -409,4 +409,33 @@ using i128 = Integer<int128>;
409409

410410
} // namespace numbers
411411

412+
namespace std {
413+
template <>
414+
struct hash<numbers::i8> {
415+
size_t operator()(const numbers::i8 &obj) const { return std::hash<int8_t>()(static_cast<int8_t>(obj)); }
416+
};
417+
418+
template <>
419+
struct hash<numbers::i16> {
420+
size_t operator()(const numbers::i16 &obj) const { return std::hash<int16_t>()(static_cast<int16_t>(obj)); }
421+
};
422+
423+
template <>
424+
struct hash<numbers::i32> {
425+
size_t operator()(const numbers::i32 &obj) const { return std::hash<int32_t>()(static_cast<int32_t>(obj)); }
426+
};
427+
428+
template <>
429+
struct hash<numbers::i64> {
430+
size_t operator()(const numbers::i64 &obj) const { return std::hash<int64_t>()(static_cast<int64_t>(obj)); }
431+
};
432+
433+
// TODO hash collision
434+
template <>
435+
struct hash<numbers::i128> {
436+
size_t operator()(const numbers::i128 &obj) const { return std::hash<numbers::int128>()(static_cast<numbers::int128>(obj)); }
437+
};
438+
439+
} // namespace std
440+
412441
#endif

Diff for: src/include/uinteger.hh

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#ifndef HEADER_UNINTEGER_H
2-
#define HEADER_UNINTEGER_H
1+
#ifndef NUMBERS_UNINTEGER_HH
2+
#define NUMBERS_UNINTEGER_HH
33

44
#include <iostream>
55
#include <limits>
@@ -348,4 +348,33 @@ using u128 = Uinteger<uint128>;
348348

349349
} // namespace numbers
350350

351+
namespace std {
352+
template <>
353+
struct hash<numbers::u8> {
354+
size_t operator()(const numbers::u8 &obj) const { return std::hash<uint8_t>()(static_cast<uint8_t>(obj)); }
355+
};
356+
357+
template <>
358+
struct hash<numbers::u16> {
359+
size_t operator()(const numbers::u16 &obj) const { return std::hash<uint16_t>()(static_cast<uint16_t>(obj)); }
360+
};
361+
362+
template <>
363+
struct hash<numbers::u32> {
364+
size_t operator()(const numbers::u32 &obj) const { return std::hash<uint32_t>()(static_cast<uint32_t>(obj)); }
365+
};
366+
367+
template <>
368+
struct hash<numbers::u64> {
369+
size_t operator()(const numbers::u64 &obj) const { return std::hash<uint64_t>()(static_cast<uint64_t>(obj)); }
370+
};
371+
372+
// TODO hash collision
373+
template <>
374+
struct hash<numbers::u128> {
375+
size_t operator()(const numbers::u128 &obj) const { return std::hash<numbers::uint128>()(static_cast<numbers::uint128>(obj)); }
376+
};
377+
378+
} // namespace std
379+
351380
#endif

Diff for: tests/integer/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*test.cc")
1+
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*.test.cc")
22

33
set(PROJECT_NAME integer_test)
44

File renamed without changes.

Diff for: tests/integer/int128_test.cc renamed to tests/integer/int128.test.cc

+11
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,14 @@ TEST(Int128Test, NumericLimits) {
484484
EXPECT_EQ(int128_min(), std::numeric_limits<int128>::lowest());
485485
EXPECT_EQ(int128_max(), std::numeric_limits<int128>::max());
486486
}
487+
488+
TEST(Int128Test, Hash) {
489+
int128 a = make_int128(34, 56);
490+
int128 b = a;
491+
std::hash<int128> hasher;
492+
493+
EXPECT_EQ(hasher(a), hasher(a));
494+
EXPECT_EQ(hasher(b), hasher(b));
495+
496+
EXPECT_EQ(hasher(a), hasher(b));
497+
}

Diff for: tests/integer/integer_test.cc renamed to tests/integer/integer.test.cc

+23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <numeric>
2+
#include <unordered_set>
23
#include <vector>
34
#include "gtest/gtest.h"
45

@@ -1128,3 +1129,25 @@ TEST(integerTest, integerSaturatingNegNoSideEffects) {
11281129
auto _ = num.saturating_neg();
11291130
ASSERT_EQ(num, tmp_num);
11301131
}
1132+
1133+
TEST(integerTest, Hash) {
1134+
i64 a = 4567983;
1135+
i64 b = a;
1136+
std::hash<i64> hasher;
1137+
1138+
EXPECT_EQ(hasher(a), hasher(a));
1139+
EXPECT_EQ(hasher(b), hasher(b));
1140+
1141+
EXPECT_EQ(hasher(a), hasher(b));
1142+
}
1143+
1144+
TEST(integerTest, UnorderedSet) {
1145+
std::unordered_set<i8> s8;
1146+
size_t cnt = 0;
1147+
for (i8 i = 0; i < i8::MAX; i = i.saturating_add(17)) {
1148+
s8.insert(i);
1149+
++cnt;
1150+
ASSERT_EQ(s8.size(), cnt);
1151+
ASSERT_EQ(s8.count(i), 1);
1152+
}
1153+
}

Diff for: tests/uinteger/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*test.cc")
1+
file(GLOB_RECURSE test_files "${CMAKE_CURRENT_SOURCE_DIR}/*.test.cc")
22

33
set(PROJECT_NAME uinteger_test)
44

File renamed without changes.

Diff for: tests/uinteger/uint128_test.cc renamed to tests/uinteger/uint128.test.cc

+13
Original file line numberDiff line numberDiff line change
@@ -392,4 +392,17 @@ TEST_F(Uint128Test, NumericLimits) {
392392
EXPECT_EQ(std::numeric_limits<numbers::uint128>::round_style, std::round_toward_zero);
393393
}
394394

395+
TEST_F(Uint128Test, Hash) {
396+
using namespace numbers;
397+
398+
uint128 a = make_uint128(56, 78);
399+
uint128 b = a;
400+
std::hash<uint128> hasher;
401+
402+
EXPECT_EQ(hasher(a), hasher(a));
403+
EXPECT_EQ(hasher(b), hasher(b));
404+
405+
EXPECT_EQ(hasher(a), hasher(b));
406+
}
407+
395408
} // namespace

Diff for: tests/uinteger/uinteger_test.cc renamed to tests/uinteger/uinteger.test.cc

+23
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "gtest/gtest.h"
44

5+
#include <unordered_set>
56
#include "test/utils.hh"
67
#include "uinteger.hh"
78

@@ -769,3 +770,25 @@ TEST(UintegerTest, UintegerWrappingNegNoSideEffects) {
769770
auto _ = num.wrapping_neg();
770771
ASSERT_EQ(num, tmp_num);
771772
}
773+
774+
TEST(UintegerTest, Hash) {
775+
u32 a = 1245679;
776+
u32 b = a;
777+
std::hash<u32> hasher;
778+
779+
EXPECT_EQ(hasher(a), hasher(a));
780+
EXPECT_EQ(hasher(b), hasher(b));
781+
782+
EXPECT_EQ(hasher(a), hasher(b));
783+
}
784+
785+
TEST(UintegerTest, UnorderedSet) {
786+
std::unordered_set<u8> s8;
787+
size_t cnt = 0;
788+
for (u8 i = u8::MIN; i < u8::MAX; i = i.saturating_add(19)) {
789+
s8.insert(i);
790+
++cnt;
791+
ASSERT_EQ(s8.size(), cnt);
792+
ASSERT_EQ(s8.count(i), 1);
793+
}
794+
}

0 commit comments

Comments
 (0)