1//! @file test_numeric.cpp
2//! @author ryftchen
3//! @brief The definitions (test_numeric) in the test module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#include <gtest/gtest.h>
8#include <syncstream>
9
10#include "application/example/include/apply_numeric.hpp"
11
12//! @brief The test module.
13namespace test // NOLINT(modernize-concat-nested-namespaces)
14{
15//! @brief Numeric-testing-related functions in the test module.
16namespace tst_num
17{
18//! @brief Print the progress of the numeric task tests.
19//! @param title - task title
20//! @param state - task state
21//! @param align - alignment width
22static void printTaskProgress(const std::string_view title, const std::string_view state, const std::uint8_t align = 50)
23{
24 std::osyncstream(std::cout) << "TEST NUMERIC: " << std::setiosflags(std::ios_base::left) << std::setfill('.')
25 << std::setw(align) << title << state << std::resetiosflags(mask: std::ios_base::left)
26 << std::setfill(' ') << std::endl;
27}
28
29//! @brief Test base of arithmetic.
30class ArithmeticTestBase : public ::testing::Test
31{
32private:
33 //! @brief Alias for the input builder.
34 using InputBuilder = application::app_num::arithmetic::InputBuilder;
35 //! @brief Prepare scenario.
36 static void prepareScenario()
37 {
38 namespace input = application::app_num::arithmetic::input;
39 fixture = std::make_unique<InputBuilder>(args: input::operandA, args: input::operandB);
40 }
41 //! @brief Reset scenario.
42 static void resetScenario() { fixture.reset(); }
43
44protected:
45 //! @brief Set up the test case.
46 static void SetUpTestSuite()
47 {
48 printTaskProgress(title, state: "BEGIN");
49 prepareScenario();
50 }
51 //! @brief Tear down the test case.
52 static void TearDownTestSuite()
53 {
54 printTaskProgress(title, state: "END");
55 resetScenario();
56 }
57
58 //! @brief Test title.
59 inline static const std::string_view title{numeric::arithmetic::description()};
60 //! @brief System under test.
61 [[no_unique_address]] const numeric::arithmetic::Arithmetic sut{};
62 //! @brief Fixture data.
63 inline static std::unique_ptr<InputBuilder> fixture{};
64 //! @brief Expected result 1.
65 static constexpr std::int32_t expRes1{0};
66 //! @brief Expected result 2.
67 static constexpr std::int32_t expRes2{92680};
68 //! @brief Expected result 3.
69 static constexpr std::int32_t expRes3{-2147395600};
70 //! @brief Expected result 4.
71 static constexpr std::int32_t expRes4{-1};
72};
73
74//! @brief Test for the addition method in the calculation of arithmetic.
75TEST_F(ArithmeticTestBase, AdditionMethod)
76{
77 ASSERT_EQ(expRes1, sut.addition(fixture->getOperands().first, fixture->getOperands().second));
78}
79
80//! @brief Test for the subtraction method in the calculation of arithmetic.
81TEST_F(ArithmeticTestBase, SubtractionMethod)
82{
83 ASSERT_EQ(expRes2, sut.subtraction(fixture->getOperands().first, fixture->getOperands().second));
84}
85
86//! @brief Test for the multiplication method in the calculation of arithmetic.
87TEST_F(ArithmeticTestBase, MultiplicationMethod)
88{
89 ASSERT_EQ(expRes3, sut.multiplication(fixture->getOperands().first, fixture->getOperands().second));
90}
91
92//! @brief Test for the division method in the calculation of arithmetic.
93TEST_F(ArithmeticTestBase, DivisionMethod)
94{
95 ASSERT_EQ(expRes4, sut.division(fixture->getOperands().first, fixture->getOperands().second));
96}
97
98//! @brief Test base of divisor.
99class DivisorTestBase : public ::testing::Test
100{
101private:
102 //! @brief Alias for the input builder.
103 using InputBuilder = application::app_num::divisor::InputBuilder;
104 //! @brief Prepare scenario.
105 static void prepareScenario()
106 {
107 namespace input = application::app_num::divisor::input;
108 fixture = std::make_unique<InputBuilder>(args: input::numberA, args: input::numberB);
109 }
110 //! @brief Reset scenario.
111 static void resetScenario() { fixture.reset(); }
112
113protected:
114 //! @brief Set up the test case.
115 static void SetUpTestSuite()
116 {
117 printTaskProgress(title, state: "BEGIN");
118 prepareScenario();
119 }
120 //! @brief Tear down the test case.
121 static void TearDownTestSuite()
122 {
123 printTaskProgress(title, state: "END");
124 resetScenario();
125 }
126
127 //! @brief Test title.
128 inline static const std::string_view title{numeric::divisor::description()};
129 //! @brief System under test.
130 [[no_unique_address]] const numeric::divisor::Divisor sut{};
131 //! @brief Fixture data.
132 inline static std::unique_ptr<InputBuilder> fixture{};
133 //! @brief Expected result.
134 const std::set<std::int32_t> expRes{1, 2, 3, 5, 6, 7, 10, 14, 15, 21, 30, 35, 42, 70, 105, 210};
135};
136
137//! @brief Test for the Euclidean method in the calculation of divisor.
138TEST_F(DivisorTestBase, EuclideanMethod)
139{
140 ASSERT_EQ(expRes, sut.euclidean(fixture->getNumbers().first, fixture->getNumbers().second));
141}
142
143//! @brief Test for the Stein method in the calculation of divisor.
144TEST_F(DivisorTestBase, SteinMethod)
145{
146 ASSERT_EQ(expRes, sut.stein(fixture->getNumbers().first, fixture->getNumbers().second));
147}
148
149//! @brief Test base of integral.
150class IntegralTestBase : public ::testing::Test
151{
152private:
153 //! @brief Alias for the input builder.
154 using InputBuilder = application::app_num::integral::InputBuilder;
155 //! @brief Prepare scenario.
156 static void prepareScenario()
157 {
158 using application::app_num::integral::input::CylindricalBessel, numeric::integral::Trapezoidal,
159 numeric::integral::Simpson, numeric::integral::Romberg, numeric::integral::Gauss,
160 numeric::integral::MonteCarlo;
161 fixture = std::make_unique<InputBuilder>(
162 args: CylindricalBessel{}, args: CylindricalBessel::range1, args: CylindricalBessel::range2, args: CylindricalBessel::exprDescr);
163 sut.clear();
164 const auto expression = fixture->getExpression();
165 sut["Trapezoidal"] = std::make_unique<Trapezoidal>(args: expression);
166 sut["Simpson"] = std::make_unique<Simpson>(args: expression);
167 sut["Romberg"] = std::make_unique<Romberg>(args: expression);
168 sut["Gauss"] = std::make_unique<Gauss>(args: expression);
169 sut["MonteCarlo"] = std::make_unique<MonteCarlo>(args: expression);
170 }
171 //! @brief Reset scenario.
172 static void resetScenario()
173 {
174 fixture.reset();
175 sut.clear();
176 }
177
178protected:
179 //! @brief Set up the test case.
180 static void SetUpTestSuite()
181 {
182 printTaskProgress(title, state: "BEGIN");
183 prepareScenario();
184 }
185 //! @brief Tear down the test case.
186 static void TearDownTestSuite()
187 {
188 printTaskProgress(title, state: "END");
189 resetScenario();
190 }
191
192 //! @brief Test title.
193 inline static const std::string_view title{numeric::integral::description()};
194 //! @brief System under test.
195 inline static std::unordered_map<std::string, std::unique_ptr<numeric::integral::Integral>> sut{};
196 //! @brief Fixture data.
197 inline static std::unique_ptr<InputBuilder> fixture{};
198 //! @brief Expected result.
199 static constexpr double expRes{1.05838};
200 //! @brief Allowable absolute error.
201 static constexpr double absErr{0.1 * ((expRes < 0.0) ? -expRes : expRes)};
202 //! @brief Default precision.
203 static constexpr double defPrec{numeric::integral::epsilon};
204};
205
206//! @brief Test for the trapezoidal method in the calculation of integral.
207TEST_F(IntegralTestBase, TrapezoidalMethod)
208{
209 const auto& trapezoidal = *sut.at(k: "Trapezoidal");
210 const auto result = trapezoidal(fixture->getRanges().first, fixture->getRanges().second, defPrec);
211 EXPECT_GT(result, expRes - absErr);
212 EXPECT_LT(result, expRes + absErr);
213}
214
215//! @brief Test for the adaptive Simpson's 1/3 method in the calculation of integral.
216TEST_F(IntegralTestBase, AdaptiveSimpsonMethod)
217{
218 const auto& simpson = *sut.at(k: "Simpson");
219 const auto result = simpson(fixture->getRanges().first, fixture->getRanges().second, defPrec);
220 EXPECT_GT(result, expRes - absErr);
221 EXPECT_LT(result, expRes + absErr);
222}
223
224//! @brief Test for the Romberg method in the calculation of integral.
225TEST_F(IntegralTestBase, RombergMethod)
226{
227 const auto& romberg = *sut.at(k: "Romberg");
228 const auto result = romberg(fixture->getRanges().first, fixture->getRanges().second, defPrec);
229 EXPECT_GT(result, expRes - absErr);
230 EXPECT_LT(result, expRes + absErr);
231}
232
233//! @brief Test for the Gauss-Legendre's 5-points method in the calculation of integral.
234TEST_F(IntegralTestBase, GaussLegendreMethod)
235{
236 const auto& gauss = *sut.at(k: "Gauss");
237 const auto result = gauss(fixture->getRanges().first, fixture->getRanges().second, defPrec);
238 EXPECT_GT(result, expRes - absErr);
239 EXPECT_LT(result, expRes + absErr);
240}
241
242//! @brief Test for the Monte-Carlo method in the calculation of integral.
243TEST_F(IntegralTestBase, MonteCarloMethod)
244{
245 const auto& monteCarlo = *sut.at(k: "MonteCarlo");
246 const auto result = monteCarlo(fixture->getRanges().first, fixture->getRanges().second, defPrec);
247 EXPECT_GT(result, expRes - absErr);
248 EXPECT_LT(result, expRes + absErr);
249}
250
251//! @brief Test base of prime.
252class PrimeTestBase : public ::testing::Test
253{
254private:
255 //! @brief Alias for the input builder.
256 using InputBuilder = application::app_num::prime::InputBuilder;
257 //! @brief Prepare scenario.
258 static void prepareScenario()
259 {
260 namespace input = application::app_num::prime::input;
261 fixture = std::make_unique<InputBuilder>(args: input::upperBound);
262 }
263 //! @brief Reset scenario.
264 static void resetScenario() { fixture.reset(); }
265
266protected:
267 //! @brief Set up the test case.
268 static void SetUpTestSuite()
269 {
270 printTaskProgress(title, state: "BEGIN");
271 prepareScenario();
272 }
273 //! @brief Tear down the test case.
274 static void TearDownTestSuite()
275 {
276 printTaskProgress(title, state: "END");
277 resetScenario();
278 }
279
280 //! @brief Test title.
281 inline static const std::string_view title{numeric::prime::description()};
282 //! @brief System under test.
283 [[no_unique_address]] const numeric::prime::Prime sut{};
284 //! @brief Fixture data.
285 inline static std::unique_ptr<InputBuilder> fixture{};
286 //! @brief Expected result.
287 //! @return expected result
288 static constexpr auto expRes()
289 {
290 // NOLINTBEGIN(readability-magic-numbers)
291 return std::vector<std::uint32_t>{
292 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
293 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
294 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
295 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
296 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
297 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
298 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
299 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997};
300 // NOLINTEND(readability-magic-numbers)
301 }
302};
303
304//! @brief Test for the Eratosthenes method in the calculation of prime.
305TEST_F(PrimeTestBase, EratosthenesMethod)
306{
307 ASSERT_EQ(expRes(), sut.eratosthenes(fixture->getUpperBound()));
308}
309
310//! @brief Test for the Euler method in the calculation of prime.
311TEST_F(PrimeTestBase, EulerMethod)
312{
313 ASSERT_EQ(expRes(), sut.euler(fixture->getUpperBound()));
314}
315} // namespace tst_num
316} // namespace test
317