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;
159 fixture = std::make_unique<InputBuilder>(
160 args: CylindricalBessel{}, args: CylindricalBessel::range1, args: CylindricalBessel::range2, args: CylindricalBessel::exprDescr);
161 sut.clear();
162 sut["Trapezoidal"] = std::make_unique<numeric::integral::Trapezoidal>(args: fixture->getExpression());
163 sut["Simpson"] = std::make_unique<numeric::integral::Simpson>(args: fixture->getExpression());
164 sut["Romberg"] = std::make_unique<numeric::integral::Romberg>(args: fixture->getExpression());
165 sut["Gauss"] = std::make_unique<numeric::integral::Gauss>(args: fixture->getExpression());
166 sut["MonteCarlo"] = std::make_unique<numeric::integral::MonteCarlo>(args: fixture->getExpression());
167 }
168 //! @brief Reset scenario.
169 static void resetScenario()
170 {
171 fixture.reset();
172 sut.clear();
173 }
174
175protected:
176 //! @brief Set up the test case.
177 static void SetUpTestSuite()
178 {
179 printTaskProgress(title, state: "BEGIN");
180 prepareScenario();
181 }
182 //! @brief Tear down the test case.
183 static void TearDownTestSuite()
184 {
185 printTaskProgress(title, state: "END");
186 resetScenario();
187 }
188
189 //! @brief Test title.
190 inline static const std::string_view title{numeric::integral::description()};
191 //! @brief System under test.
192 inline static std::unordered_map<std::string, std::unique_ptr<numeric::integral::Integral>> sut{};
193 //! @brief Fixture data.
194 inline static std::unique_ptr<InputBuilder> fixture{};
195 //! @brief Expected result.
196 static constexpr double expRes{1.05838};
197 //! @brief Allowable absolute error.
198 static constexpr double absErr{0.1 * ((expRes < 0.0) ? -expRes : expRes)};
199 //! @brief Default precision.
200 static constexpr double defPrec{numeric::integral::epsilon};
201};
202
203//! @brief Test for the trapezoidal method in the calculation of integral.
204TEST_F(IntegralTestBase, TrapezoidalMethod)
205{
206 const auto& trapezoidal = *sut.at(k: "Trapezoidal");
207 const auto result = trapezoidal(fixture->getRanges().first, fixture->getRanges().second, defPrec);
208 EXPECT_GT(result, expRes - absErr);
209 EXPECT_LT(result, expRes + absErr);
210}
211
212//! @brief Test for the adaptive Simpson's 1/3 method in the calculation of integral.
213TEST_F(IntegralTestBase, AdaptiveSimpsonMethod)
214{
215 const auto& simpson = *sut.at(k: "Simpson");
216 const auto result = simpson(fixture->getRanges().first, fixture->getRanges().second, defPrec);
217 EXPECT_GT(result, expRes - absErr);
218 EXPECT_LT(result, expRes + absErr);
219}
220
221//! @brief Test for the Romberg method in the calculation of integral.
222TEST_F(IntegralTestBase, RombergMethod)
223{
224 const auto& romberg = *sut.at(k: "Romberg");
225 const auto result = romberg(fixture->getRanges().first, fixture->getRanges().second, defPrec);
226 EXPECT_GT(result, expRes - absErr);
227 EXPECT_LT(result, expRes + absErr);
228}
229
230//! @brief Test for the Gauss-Legendre's 5-points method in the calculation of integral.
231TEST_F(IntegralTestBase, GaussLegendreMethod)
232{
233 const auto& gauss = *sut.at(k: "Gauss");
234 const auto result = gauss(fixture->getRanges().first, fixture->getRanges().second, defPrec);
235 EXPECT_GT(result, expRes - absErr);
236 EXPECT_LT(result, expRes + absErr);
237}
238
239//! @brief Test for the Monte-Carlo method in the calculation of integral.
240TEST_F(IntegralTestBase, MonteCarloMethod)
241{
242 const auto& monteCarlo = *sut.at(k: "MonteCarlo");
243 const auto result = monteCarlo(fixture->getRanges().first, fixture->getRanges().second, defPrec);
244 EXPECT_GT(result, expRes - absErr);
245 EXPECT_LT(result, expRes + absErr);
246}
247
248//! @brief Test base of prime.
249class PrimeTestBase : public ::testing::Test
250{
251private:
252 //! @brief Alias for the input builder.
253 using InputBuilder = application::app_num::prime::InputBuilder;
254 //! @brief Prepare scenario.
255 static void prepareScenario()
256 {
257 namespace input = application::app_num::prime::input;
258 fixture = std::make_unique<InputBuilder>(args: input::upperBound);
259 }
260 //! @brief Reset scenario.
261 static void resetScenario() { fixture.reset(); }
262
263protected:
264 //! @brief Set up the test case.
265 static void SetUpTestSuite()
266 {
267 printTaskProgress(title, state: "BEGIN");
268 prepareScenario();
269 }
270 //! @brief Tear down the test case.
271 static void TearDownTestSuite()
272 {
273 printTaskProgress(title, state: "END");
274 resetScenario();
275 }
276
277 //! @brief Test title.
278 inline static const std::string_view title{numeric::prime::description()};
279 //! @brief System under test.
280 [[no_unique_address]] const numeric::prime::Prime sut{};
281 //! @brief Fixture data.
282 inline static std::unique_ptr<InputBuilder> fixture{};
283 //! @brief Expected result.
284 //! @return expected result
285 static constexpr auto expRes()
286 {
287 // NOLINTBEGIN(readability-magic-numbers)
288 return std::vector<std::uint32_t>{
289 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
290 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
291 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
292 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
293 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
294 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
295 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
296 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997};
297 // NOLINTEND(readability-magic-numbers)
298 }
299};
300
301//! @brief Test for the Eratosthenes method in the calculation of prime.
302TEST_F(PrimeTestBase, EratosthenesMethod)
303{
304 ASSERT_EQ(expRes(), sut.eratosthenes(fixture->getUpperBound()));
305}
306
307//! @brief Test for the Euler method in the calculation of prime.
308TEST_F(PrimeTestBase, EulerMethod)
309{
310 ASSERT_EQ(expRes(), sut.euler(fixture->getUpperBound()));
311}
312} // namespace tst_num
313} // namespace test
314