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 <atomic>
10 #include <cstddef>
11 #include <random>
12 #include <thread>
13 #include <tuple>
14 #include <type_traits>
15 #include <vector>
16 
17 #include <mp++/config.hpp>
18 #include <mp++/integer.hpp>
19 
20 #include "catch.hpp"
21 #include "test_utils.hpp"
22 
23 static const int ntries = 1000;
24 
25 // NOLINTNEXTLINE(google-build-using-namespace)
26 using namespace mppp;
27 // NOLINTNEXTLINE(google-build-using-namespace)
28 using namespace mppp_test;
29 
30 using sizes = std::tuple<std::integral_constant<std::size_t, 1>, std::integral_constant<std::size_t, 2>,
31                          std::integral_constant<std::size_t, 3>, std::integral_constant<std::size_t, 6>,
32                          std::integral_constant<std::size_t, 10>>;
33 
34 struct cache_tester {
35     template <typename S>
operator ()cache_tester36     inline void operator()(const S &) const
37     {
38         using integer = integer<S::value>;
39         std::atomic<bool> flag{true};
40         // Run a variety of tests with operands with x number of limbs.
41         auto random_xy = [&flag](unsigned x) {
42             auto checker = [&flag]() {
43 #if defined(MPPP_HAVE_THREAD_LOCAL)
44                 const auto &mpzc = detail::get_thread_local_mpz_cache();
45                 for (auto s : mpzc.sizes) {
46                     if (s) {
47                         flag.store(false);
48                     }
49                 }
50 #endif
51             };
52             // NOLINTNEXTLINE(cert-err58-cpp, cert-msc32-c, cert-msc51-cpp)
53             std::mt19937 rng;
54             rng.seed(x);
55             std::uniform_int_distribution<int> sdist(0, 1);
56             detail::mpz_raii tmp;
57             std::vector<integer> v_int;
58             for (int i = 0; i < ntries; ++i) {
59                 random_integer(tmp, x, rng);
60                 v_int.emplace_back(&tmp.m_mpz);
61                 if (sdist(rng)) {
62                     v_int.back().neg();
63                 }
64                 if (sdist(rng)) {
65                     // Promote sometimes, if possible.
66                     v_int.back().promote();
67                 }
68             }
69             v_int.resize(0);
70             free_integer_caches();
71             free_integer_caches();
72             free_integer_caches();
73             checker();
74             for (int i = 0; i < ntries; ++i) {
75                 random_integer(tmp, x, rng);
76                 v_int.emplace_back(&tmp.m_mpz);
77                 if (sdist(rng)) {
78                     v_int.back().neg();
79                 }
80                 if (sdist(rng)) {
81                     // Promote sometimes, if possible.
82                     v_int.back().promote();
83                 }
84             }
85             v_int.resize(0);
86             free_integer_caches();
87             free_integer_caches();
88             free_integer_caches();
89             checker();
90             for (int i = 0; i < ntries; ++i) {
91                 random_integer(tmp, x, rng);
92                 v_int.emplace_back(&tmp.m_mpz);
93                 if (sdist(rng)) {
94                     v_int.back().neg();
95                 }
96                 if (sdist(rng)) {
97                     // Promote sometimes, if possible.
98                     v_int.back().promote();
99                 }
100             }
101             free_integer_caches();
102             free_integer_caches();
103             free_integer_caches();
104         };
105 
106         std::thread t0(random_xy, 0);
107         std::thread t1(random_xy, 1);
108         std::thread t2(random_xy, 2);
109         std::thread t3(random_xy, 3);
110         std::thread t4(random_xy, 4);
111         t0.join();
112         t1.join();
113         t2.join();
114         t3.join();
115         t4.join();
116     }
117 };
118 
119 TEST_CASE("caches")
120 {
121     tuple_for_each(sizes{}, cache_tester{});
122 }
123