1 // Copyright 2016-2021 Francesco Biscani (bluescarni@gmail.com)
2 //
3 // This file is part of the mp++ library.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla
6 // Public License v. 2.0. If a copy of the MPL was not distributed
7 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <cstddef>
10 #include <functional>
11 #include <random>
12 #include <tuple>
13 #include <type_traits>
14 
15 #include <gmp.h>
16 
17 #include <mp++/integer.hpp>
18 
19 #include "catch.hpp"
20 #include "test_utils.hpp"
21 
22 static const int ntries = 1000;
23 
24 // NOLINTNEXTLINE(google-build-using-namespace)
25 using namespace mppp;
26 // NOLINTNEXTLINE(google-build-using-namespace)
27 using namespace mppp_test;
28 
29 using sizes = std::tuple<std::integral_constant<std::size_t, 1>, std::integral_constant<std::size_t, 2>,
30                          std::integral_constant<std::size_t, 3>, std::integral_constant<std::size_t, 6>,
31                          std::integral_constant<std::size_t, 10>>;
32 
33 // NOLINTNEXTLINE(cert-err58-cpp, cert-msc32-c, cert-msc51-cpp, cppcoreguidelines-avoid-non-const-global-variables)
34 static std::mt19937 rng;
35 
36 struct hash_tester {
37     template <typename S>
operator ()hash_tester38     inline void operator()(const S &) const
39     {
40         using integer = integer<S::value>;
41         const std::hash<integer> hasher{};
42         integer n1, n2;
43         REQUIRE((hash(n1) == 0u));
44         REQUIRE((hasher(n1) == 0u));
45         n1.promote();
46         REQUIRE((hash(n1) == 0u));
47         REQUIRE((hasher(n1) == 0u));
48         n1 = integer{12};
49         n2 = n1;
50         REQUIRE(n2.is_static());
51         n1.promote();
52         REQUIRE(n1.is_dynamic());
53         REQUIRE((hash(n1) == hash(n2)));
54         REQUIRE((hasher(n1) == hash(n2)));
55         n1 = integer{-12};
56         n2 = n1;
57         REQUIRE(n2.is_static());
58         n1.promote();
59         REQUIRE(n1.is_dynamic());
60         REQUIRE((hash(n1) == hash(n2)));
61         REQUIRE((hash(n1) == hasher(n2)));
62         detail::mpz_raii tmp;
63         std::uniform_int_distribution<int> sdist(0, 1);
64         // Run a variety of tests with operands with x number of limbs.
65         auto random_xy = [&](unsigned x) {
66             for (int i = 0; i < ntries; ++i) {
67                 random_integer(tmp, x, rng);
68                 n1 = integer(detail::mpz_to_str(&tmp.m_mpz));
69                 if (sdist(rng)) {
70                     n1.neg();
71                 }
72                 n2 = n1;
73                 if (n2.is_static()) {
74                     n1.promote();
75                 }
76                 REQUIRE((hash(n1) == hash(n2)));
77                 REQUIRE((hasher(n1) == hash(n2)));
78             }
79         };
80 
81         random_xy(0);
82         random_xy(1);
83         random_xy(2);
84         random_xy(3);
85         random_xy(4);
86     }
87 };
88 
89 TEST_CASE("hash")
90 {
91     tuple_for_each(sizes{}, hash_tester{});
92 }
93